﻿using Logistics.Web.Infrastructure;
using Logistics.Web.Models.Common;
using Logistics.Web.Models.Order;
using Logistics.Web.Utilities;
using Newtonsoft.Json;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Directory;
using Nop.Core.Domain.Localization;
using Nop.Core.Domain.Logistics;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Shipping;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Directory;
using Nop.Services.Events;
using Nop.Services.Helpers;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Logistics;
using Nop.Services.Messages;
using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Services.Security;
using Nop.Services.Shipping;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Security;
using StackExchange.Profiling;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Nop.Core.Domain.Payments;
using SpotsHub.Gateway.Extensions;
using SpotsHub.Gateway.Models.Customer;
using Telerik.Web.Mvc;
using SummaryModel = Logistics.Web.Models.Order.SummaryModel;

namespace Logistics.Web.Controllers
{
    [RequireHttpsAttribute]
    [MobileRedirect]
    [NopHttpsRequirement(SslRequirement.Yes)]
    public class OrderController : BaseController
    {
        #region Fields

        private readonly IWorkContext _workContext;
        private readonly IPermissionService _permissionService;
        private readonly ICustomerActivityService _customerActivityService;
        private readonly ILogisticsService _logisticsService;
        private readonly IDateTimeHelper _dateTimeHelper;
        private readonly ILocalizationService _localizationService;
        private readonly IGenericAttributeService _genericAttributeService;
        private readonly ICustomerService _customerService;
        private readonly IAddressService _addressService;
        private readonly IProductService _productService;
        private readonly IProductAttributeService _productAttributeService;
        private readonly IProductAttributeParser _productAttributeParser;
        private readonly IPaymentService _paymentService;
        private readonly Nop.Services.Logistics.IOrderProcessingService _orderProcessingService;
        private readonly ICurrencyService _currencyService;
        private readonly IWorkflowMessageService _workflowMessageService;
        private readonly IShipmentService _shipmentService;
        private readonly IPdfService _pdfService;
        private readonly IInvoiceService _invoiceService;
        private readonly InvoiceSettings _invoiceSettings;
        private readonly IMeasureService _measureService;
        private readonly IGiftCardService _giftCardService;
        private readonly IHolidayService _holidayService;
        private readonly IEventPublisher _eventPublisher;
        private readonly IProductAttributeFormatter _productAttributeFormatter;

        private readonly IWebHelper _webHelper;
        private readonly IPriceFormatter _priceFormatter;

        private readonly HttpContextBase _httpContext;

        private readonly CurrencySettings _currencySettings;
        private readonly LogisticsInformationSettings _logisticsInformationSettings;
        private readonly MeasureSettings _measureSettings;

        private readonly ICountryService _countryService;
        private readonly IStateProvinceService _stateProvinceService;
        private readonly LocalizationSettings _localizationSettings;
        private readonly IOrderService _orderService;

        private readonly PaymentSettings _paymentSettings;
        //private readonly IGlobalPaymentsService _globalPaymentsService;

        private readonly ILogger _logger;
        //private readonly IGlobalPaymentsService _globalPaymentService;

        private const string ReturnRequestGeneryKeyGroup = "ReturnRequestLogisticOrder";
        #endregion

        #region Ctor

        public OrderController(
            IWorkContext workContext,
            IPermissionService permissionService,
            ICustomerActivityService customerActivityService,
            ILogisticsService logisticsService,
            IDateTimeHelper dateTimeHelper,
            ILocalizationService localizationService,
            IGenericAttributeService genericAttributeService,
            ICustomerService customerService,
            IAddressService addressService,
            IProductService productService,
            IProductAttributeService productAttributeService,
            IPaymentService paymentService,
            IProductAttributeParser productAttributeParser,
            ICurrencyService currencyService,
            Nop.Services.Logistics.IOrderProcessingService orderProcessingService,
            IWorkflowMessageService workflowMessageService,
            ILogisticsService logisticsSerivce,
            IShipmentService shipmentService,
            IPdfService pdfService,
            IInvoiceService invoiceSerivce,
            IMeasureService measureSerivce,
            IGiftCardService giftCardService,
            IHolidayService holidayService,
            IEventPublisher eventPublisher,
            IProductAttributeFormatter productAttributeFormatter,
            IWebHelper webHelper,
            IPriceFormatter priceFormatter,
            HttpContextBase httpContext,
            InvoiceSettings invoiceSettings,
            CurrencySettings currencySettings,
            MeasureSettings measureSettings,
            ICountryService countryService,
            IStateProvinceService stateProvinceService,
            LogisticsInformationSettings logisticsInformationSettings,
            LocalizationSettings localizationSettings,
            IOrderService orderService,
            PaymentSettings paymentSettings,

            ILogger logger
            //IGlobalPaymentsService globalPaymentsService
            )
        {
            this._workContext = workContext;
            this._orderProcessingService = orderProcessingService;
            this._permissionService = permissionService;
            this._customerActivityService = customerActivityService;
            this._logisticsService = logisticsService;
            this._dateTimeHelper = dateTimeHelper;
            this._localizationService = localizationService;
            this._genericAttributeService = genericAttributeService;
            this._customerService = customerService;
            this._addressService = addressService;
            this._productService = productService;
            this._productAttributeService = productAttributeService;
            this._paymentService = paymentService;
            this._productAttributeParser = productAttributeParser;
            this._currencyService = currencyService;
            this._workflowMessageService = workflowMessageService;
            this._logisticsService = logisticsSerivce;
            this._shipmentService = shipmentService;
            this._measureService = measureSerivce;
            this._pdfService = pdfService;
            this._invoiceService = invoiceSerivce;
            this._giftCardService = giftCardService;
            this._holidayService = holidayService;
            this._eventPublisher = eventPublisher;
            this._productAttributeFormatter = productAttributeFormatter;
            this._webHelper = webHelper;
            this._priceFormatter = priceFormatter;
            this._httpContext = httpContext;
            this._invoiceSettings = invoiceSettings;
            this._currencySettings = currencySettings;
            this._logisticsInformationSettings = logisticsInformationSettings;
            this._measureSettings = measureSettings;
            this._countryService = countryService;
            this._stateProvinceService = stateProvinceService;
            this._localizationSettings = localizationSettings;
            this._orderService = orderService;
            this._paymentSettings = paymentSettings;
            this._logger = logger;
            //this._globalPaymentService = globalPaymentsService;
            SystemKeyword = "Logistics.Order";
        }

        #endregion

        #region Utility

        public string RenderMessages(List<string> notices = null, List<string> warnings = null)
        {
            var messages = "";
            if (notices != null && notices.Count > 0)
            {
                messages += "<div class='alert alert-success alert-dismissible' role='alert'><button type='button' class='close' data-dismiss='alert' aria-label='Close'><span aria-hidden='true'>&times;</span></button><ul class='notices'>";
                foreach (var n in notices)
                {
                    messages += "<li>" + n + "</li>";
                }
                messages += "</ul></div>";
            }
            if (warnings != null && warnings.Count > 0)
            {
                messages += "<div class='alert alert-warning alert-dismissible' role='alert'><button type='button' class='close' data-dismiss='alert' aria-label='Close'><span aria-hidden='true'>&times;</span></button><ul class='notices'>";
                foreach (var w in warnings)
                {
                    messages += "<li>" + w + "</li>";
                }
                messages += "</ul></div>";
            }
            if (!ModelState.IsValid)
            {
                messages += "<div class='alert alert-danger alert-dismissible' role='alert'><button type='button' class='close' data-dismiss='alert' aria-label='Close'><span aria-hidden='true'>&times;</span></button><ul class='errors'>";
                var errorlist = ModelState.Select(x => x.Value.Errors)
                    .Where(y => y.Count > 0)
                    .ToList();
                foreach (var err in errorlist)
                {
                    foreach (var e in err)
                    {
                        messages += "<li>" + e.ErrorMessage + "</li>";
                    }
                }
                messages += "</ul></div>";
            }
            return messages;
        }

        #endregion

        #region View

        [NonAction]
        protected object GetJsonView(string viewName, object model, List<string> notices = null, List<string> warnings = null, int packageId = -1, bool? isGiftCardValid = null, bool orderCompleted = false)
        {
            var customer = _workContext.CurrentCustomer;
            try
            {
                var oi = LoadOrderInformation(customer);
                var li = LoadCurrentLocation(oi);
                var pi = LoadPaymentInformation(customer);
                if (notices == null)
                    notices = new List<string>();
                if (warnings == null)
                    warnings = new List<string>();
                var creditCardId = pi != null && pi.CreditCardId > 0 ? pi.CreditCardId : 0;
                var displayGiftCard = _logisticsInformationSettings.EnableGiftCard && (viewName == CommonValues.DetailViewName);
                var sumModel = new SummaryModel();
                if (!orderCompleted && viewName != CommonValues.AccessDeniedViewName && viewName != CommonValues.GetPaymentViewName)
                {
                    if (creditCardId != 0 && creditCardId > 0)
                    {

                        sumModel = PrepareSummaryModel(_workContext.CurrentCustomer, creditCardId, displayGiftCard, isGiftCardValid);
                    }
                    else
                    {
                        var processPaymentRequest = _httpContext.Session[PaymentContants.PaymentInfoSessionKey] as ProcessPaymentRequest;
                        if (processPaymentRequest != null && processPaymentRequest.CreditCardId != 0)
                        {
                            sumModel = PrepareSummaryModel(_workContext.CurrentCustomer, processPaymentRequest.CreditCardId, displayGiftCard,
                                isGiftCardValid);
                        }
                        else
                        {
                            sumModel = PrepareSummaryModel(_workContext.CurrentCustomer, 0, displayGiftCard,
                               isGiftCardValid);
                        }
                    }

                    if (packageId > 0)
                    {
                        decimal cost = CalculatePackageCost(li, customer, packageId);
                        sumModel.CurrentPackageTotal = cost;
                        sumModel.SubTotalExcCurrentPackage = sumModel.SubTotalExcSurchargeTax - cost;
                    }
                    else
                    {
                        sumModel.CurrentPackageTotal = -1;
                    }
                }
                if (viewName == CommonValues.PackageInfoViewName)
                    sumModel.ShowCurrentPackage = true;
                if (viewName == CommonValues.FinalizeViewName)
                {
                    model = sumModel;

                    // Check model for finalizing order
                    if (sumModel.Locations.Count < 1)
                        ModelState.AddModelError("", "Order is invalid. There is no location information for this order.");
                    else
                    {
                        var loc = sumModel.Locations.First();
                        if (loc.Packages == null || loc.Packages.Count < 1)
                            ModelState.AddModelError("", "There is no package in the order.");

                        if (loc.UserAddress == null || _addressService.GetAddressById(loc.UserAddress.Id) == null)
                            ModelState.AddModelError("", "Pickup address is invalid.");
                        else if (!loc.UserAddress.Type.HasValue || loc.UserAddress.Type.Value == AddressType.Unknown)
                            warnings.Add("Address type for pickup address is invalid.");

                        if (loc.Address == null || _addressService.GetAddressById(loc.Address.Id) == null)
                            ModelState.AddModelError("", "Delivery address is invalid.");
                        else if (!loc.Address.Type.HasValue || loc.Address.Type.Value == AddressType.Unknown)
                            warnings.Add("Address type for delivery address is invalid.");

                        if (loc.UserAddress != null && loc.Address != null && loc.UserAddress.Id == loc.Address.Id)
                            warnings.Add("Pickup address and delivery address appear to be the same. Please check your order before proceeding.");

                        if (!sumModel.IsPaymentExempt && sumModel.Total > 0)
                        {
                            if (pi == null || pi.CustomerId != customer.Id || pi.OrderTotal != sumModel.Total)
                                ModelState.AddModelError("", "Payment information is invalid.");
                        }
                    }
                }

                var ordermenuhtml = "";
                if (viewName != CommonValues.PendingOrderViewName && viewName != CommonValues.AccessDeniedViewName && viewName != CommonValues.GetPaymentViewName)
                {
                    var orderMenuModel = PrepareOrderMenuModel(viewName, li, oi, orderCompleted);
                    orderMenuModel.IsPaymentExempt = _orderProcessingService.IsPaymentExempt(customer);
                    ordermenuhtml = this.RenderPartialViewToString(CommonValues.OrderMenuViewName, orderMenuModel);
                }

                var sumhtml = "";
                if ((viewName == CommonValues.PickupAddressViewName || viewName == CommonValues.DeliveryAddressViewName || viewName == CommonValues.PackageInfoViewName || viewName == CommonValues.ShippingOptionViewName || viewName == CommonValues.BillingAddressViewName || viewName == CommonValues.PaymentInfoViewName || viewName == CommonValues.FinalizeViewName) && !orderCompleted)
                    sumhtml = this.RenderPartialViewToString(CommonValues.SummaryViewName, sumModel);
                var menuitem = CommonValues.PlaceOrderMenuItem;

                if (viewName != CommonValues.PendingOrderViewName)
                {
                    Response.Cookies.Add(new HttpCookie("logistics-" + customer.Id)
                    {
                        Expires = DateTime.Now.AddMinutes(15),
                        Value = DateTime.Now.ToString()
                    });
                }

                return new
                {
                    name = viewName,
                    html = this.RenderPartialViewToString(viewName, model),
                    sumhtml = sumhtml,
                    ordermenuhtml = ordermenuhtml,
                    messages = RenderMessages(notices, warnings),
                    haspending = _orderProcessingService.HasPendingOrders(customer),
                    menuitem = menuitem,
                    packageid = packageId
                };
            }
            catch (Exception exc)
            {
                LogException(exc);

                //return FatalErrorJsonView(exc.Message, customer, exc, true);
                return new
                {
                    name = CommonValues.FatalErrorName,
                    html = this.RenderPartialViewToString(CommonValues.ErrorViewName),
                    messages = RenderMessages(notices, warnings)
                };
            }
        }

        [NonAction]
        protected ActionResult GetPackageView(LocationInformation li, int? packageId = null)
        {
            if (li.Packages.Count > 0)
            {
                var pi = new PackageInformation();
                if (!packageId.HasValue)
                {
                    pi = li.Packages.OrderBy(p => p.Id).Last();
                }
                else
                {
                    pi = li.Packages.FirstOrDefault(p => p.Id == packageId);
                    if (pi == null)
                    {
                        pi = li.Packages.OrderBy(p => p.Id).Last();
                    }
                }
                var model = PreparePackageModel(pi, li.Packages.Where(p => p.Id != pi.Id));
                model.InsuranceEnabled = InsuranceEnabled(li);
                return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, pi.Id));
            }
            else
            {
                var model = PreparePackageModel(null, null);
                if (model.PackageId == 0)
                {
                    model.PackageId = 1;
                }
                model.InsuranceEnabled = InsuranceEnabled(li);
                return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, 1));
            }
        }

        #endregion

        #region Model

        [NonAction]
        protected void PreparePickupTime(LocationInformation li, LocationAddressModel model)
        {
            // Get avaliable dates
            var customer = _workContext.CurrentCustomer;

            var canPickupDate = model != null && model.PickupTimeWindowId.HasValue && model.PickupDate.HasValue;

            var dateTime = _dateTimeHelper.ConvertToUserTime(DateTime.UtcNow, DateTimeKind.Utc);
            var date = dateTime.Date;
            //var time = dateTime.Hour + _logisticsInformationSettings.AllowedHoursForPickup + (dateTime.Minute > 30 ? 1 : 0);
            int days = _logisticsInformationSettings.AvaliableDaysForPickup;
            var rawTimeWindows = _logisticsService.GetAllShippingTimeWindows();
            var timeWindows = ShippingTimeWindowEx.ConvertShippingTimeWindowEx(rawTimeWindows).Where(t => t.AllowOrderType.Contains((int)OrderKind.Express)).ToList();
            var stateProvinceId = (customer.ShippingAddress == null || customer.ShippingAddress.StateProvinceId == null) ?
                _workContext.CurrentCustomer.Region.Districts.First().StateProvinceId : customer.ShippingAddress.StateProvinceId;

            model.AvaliablePickupDateTimeWindows = new LocationAddressModel.PickupDateTimeWindow[days, timeWindows.Count];
            model.AvaliablePickupDates = new List<string>();
            model.AvaliablePickupTimeWindows = new List<string>(timeWindows.Select(tw => tw.Description));

            if (model != null && !model.PickupDate.HasValue && !model.PickupTimeWindowId.HasValue)
            {
                if (li != null && li.PickupTimeWindowId.HasValue && li.PickupDate.HasValue)
                {
                    model.PickupDate = li.PickupDate;
                    model.PickupTimeWindowId = li.PickupTimeWindowId;
                }
            }

            model.HasPickupDate = (li != null && li.PickupTimeWindowId.HasValue && li.PickupDate.HasValue);

            int avaliableDays = 0;
            for (int i = 0; i < 100 && avaliableDays < days; i++, date = date.AddDays(1))
            {
                if (date.DayOfWeek == DayOfWeek.Sunday || date.DayOfWeek == DayOfWeek.Saturday || _holidayService.IsHoliday(date, stateProvinceId))
                {
                    continue;
                }

                bool isToday = false;
                if (i == 0)
                {
                    // Today
                    isToday = true;
                    bool hasAvaliableTime = false;
                    for (int j = 0; j < timeWindows.Count; j++)
                    {
                        var cutoffTime = new DateTime(date.Year, date.Month, date.Day).AddMinutes(timeWindows[j].SameDayCutoff);
                        if (cutoffTime >= dateTime)
                        {
                            hasAvaliableTime = true;
                            break;
                        }
                    }
                    if (!hasAvaliableTime)
                    {
                        continue;
                    }
                }

                model.AvaliablePickupDates.Add(date.ToString("MM/dd"));
                for (int j = 0; j < timeWindows.Count; j++)
                {
                    var cutoffTime = new DateTime(date.Year, date.Month, date.Day).AddMinutes(timeWindows[j].SameDayCutoff);
                    model.AvaliablePickupDateTimeWindows[avaliableDays, j] = new LocationAddressModel.PickupDateTimeWindow()
                    {
                        CanSelect = (!isToday) || (cutoffTime >= dateTime),
                        PickupDate = date,
                        PickupTimeWindow = timeWindows[j].Id,
                        Selected = false
                    };
                    if (model != null && model.PickupTimeWindowId.HasValue && model.PickupDate.HasValue)
                    {
                        model.AvaliablePickupDateTimeWindows[avaliableDays, j].Selected = timeWindows[j].Id == model.PickupTimeWindowId && date == model.PickupDate.Value;
                    }
                }
                avaliableDays++;
            }
        }

        private PickupDateInfomation GetPickupDateInfomation(int addressTypeId, bool canPickupDate = true)
        {
            var dateTime = _dateTimeHelper.ConvertToUserTime(DateTime.UtcNow, DateTimeKind.Utc);
            var date = dateTime.Date;
            var time = dateTime.Hour + _logisticsInformationSettings.AllowedHoursForPickup + (dateTime.Minute > 30 ? 1 : 0);
            var days = _logisticsInformationSettings.AvaliableDaysForPickup;


            var timeWindows = _logisticsService.GetAllShippingTimeWindows();
            timeWindows = timeWindows.Where(x => x.AllowAddressType.Contains(addressTypeId.ToString()) && x.AllowOrderType.Contains(((int)OrderKind.Express).ToString())).ToList();
            // var timeWindows = _logisticsService.GetAllShippingTimeWindows();

            var customer = _workContext.CurrentCustomer;
            var stateProvinceId = (customer.ShippingAddress == null || customer.ShippingAddress.StateProvinceId == null) ?
              _workContext.CurrentCustomer.Region.Districts.First().StateProvinceId : customer.ShippingAddress.StateProvinceId;

            var model = new PickupDateInfomation(days, timeWindows.Count);
            model.AvaliablePickupTimeWindows = new List<string>(timeWindows.Select(tw => tw.Description));

            int avaliableDays = 0;
            for (int i = 0; i < 100 && avaliableDays < days; i++, date = date.AddDays(1))
            {
                if (date.DayOfWeek == DayOfWeek.Sunday || date.DayOfWeek == DayOfWeek.Saturday || _holidayService.IsHoliday(date, stateProvinceId))
                {
                    continue;
                }

                bool isToday = false;
                if (i == 0)
                {
                    // Today
                    isToday = true;
                    bool hasAvaliableTime = false;
                    for (int j = 0; j < timeWindows.Count; j++)
                    {
                        if (timeWindows[j].NoEarlyThan >= time)
                        {
                            hasAvaliableTime = true;
                            break;
                        }
                    }
                    if (!hasAvaliableTime)
                    {
                        continue;
                    }
                }

                model.AvaliablePickupDates.Add(date.ToString("MM/dd"));
                for (int j = 0; j < timeWindows.Count; j++)
                {
                    model.AvaliablePickupDateTimeWindows[avaliableDays, j] = new PickupDateInfomation.PickupDateTimeWindow()
                    {
                        CanSelect = (!isToday) || (timeWindows[j].NoEarlyThan >= time || (timeWindows[j].AllowAddressType.Contains(addressTypeId.ToString()))),
                        PickupDate = date,
                        PickupTimeWindow = timeWindows[j].Id,
                        Selected = false
                    };

                    if (canPickupDate)
                    {
                        model.AvaliablePickupDateTimeWindows[avaliableDays, j].Selected = timeWindows[j].Id == model.PickupTimeWindowId && date == model.PickupDate.Value;
                    }
                }
                avaliableDays++;
            }

            return model;
        }

        [HttpPost]
        public string GetPickupDateInfomation(string addressType)
        {
            var addressTypeId = int.Parse(addressType);

            var pickupDates = GetPickupDateInfomation(addressTypeId);

            return JsonConvert.SerializeObject(pickupDates);
        }

        [NonAction]
        protected PackageListModel PreparePackageListModel(int customerId)
        {
            var model = new PackageListModel();

            var invoices = _invoiceService.GetInvoicesByCustomerId(customerId);

            if (invoices != null && invoices.Count > 0)
            {
                model.CustomProperties.Add("ShowInvoice", true);
            }

            return model;
        }

        [NonAction]
        protected AddressModel PrepareAddressModel(Address address, Region region = null)
        {
            var model = new AddressModel();
            if (address == null)
            {
                address = new Address();
            }
            else
            {
                model.Id = address.Id;
            }
            model.Company = address.Company != null ? address.Company : "";
            model.FirstName = address.FirstName != null ? address.FirstName : "";
            model.LastName = address.LastName != null ? address.LastName : "";
            model.Address1 = address.Address1 != null ? address.Address1 : "";
            model.Address2 = address.Address2 != null ? address.Address2 : "";
            model.City = address.City != null ? address.City : "";

            if (address.Type.HasValue && address.Type.Value != AddressType.Unknown)
            {
                model.Type = address.Type.Value;
            }

            //TODO: remove hard code for province
            if (address.StateProvince != null)
            {
                model.StateProvinceId = address.StateProvinceId;
                model.StateProvinceName = address.StateProvince.GetLocalized(x => x.Name);
            }
            else if (region != null)
            {
                var p = AddressHelper.GetProvinceByRegion(region);
                model.StateProvinceId = p.Id;
                model.StateProvinceName = p.GetLocalized(x => x.Name);
            }
            else
            {
                model.StateProvinceName = address.StateProvince == null ? AddressHelper.GetDefaultProvinceName() : address.StateProvince.Name;
                model.StateProvinceId = address.StateProvinceId != null ? address.StateProvinceId : 0;
            }

            model.ZipPostalCode = address.ZipPostalCode != null ? address.ZipPostalCode : "";

            //TODO: remove hard code for country
            model.CountryName = address.Country == null ? AddressHelper.GetDefaultCountryName() : address.Country.Name;
            model.CountryId = address.CountryId != null ? address.CountryId : 0;
            model.Email = address.Email != null ? address.Email : "";
            model.CellPhoneNumber = address.CellPhoneNumber != null ? address.CellPhoneNumber : "";
            model.PhoneNumber = address.PhoneNumber != null ? address.PhoneNumber : "";

            return model;
        }

        [NonAction]
        protected void AddressModelToAddress(AddressModel model, Address address, bool permanentAddress = false)
        {
            address.FirstName = model.FirstName;
            address.LastName = model.LastName;
            address.Address2 = model.Address2;
            address.PhoneNumber = model.PhoneNumber;
            address.CellPhoneNumber = model.CellPhoneNumber;
            address.Type = model.Type;

            if (!permanentAddress)
            {
                address.Email = model.Email;
                address.Company = model.Company;
                if (model.CountryId > 0)
                {
                    address.CountryId = model.CountryId;
                }
                address.StateProvinceId = model.StateProvinceId > 0 ? model.StateProvinceId : address.StateProvinceId;
                address.City = model.City;
                address.Address1 = model.Address1;
                address.ZipPostalCode = model.ZipPostalCode;
                address.FaxNumber = model.FaxNumber;
            }
        }

        [NonAction]
        protected SummaryModel PrepareSummaryModel(Customer customer, int creditCardId = 0, bool showGiftCardBox = false, bool? isGiftCardValid = null)
        {
            OrderInformation oi = LoadOrderInformation(customer);
            if (oi == null)
            {
                throw new ArgumentNullException("oi");
            }

            var model = new SummaryModel();
            model.CreditCardId = creditCardId;
            model.UserId = customer.Id;
            if (oi.OrderId.HasValue)
                model.OrderId = oi.OrderId.Value;
            model.SubTotalExcSurchargeTax = 0M;
            model.TotalPackages = 0;
            model.Discount = 0;
            model.IsPaymentExempt = _orderProcessingService.IsPaymentExempt(customer);
            var appliedGiftCards = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer)
                    .Where(gc => gc.GiftCardType == GiftCardType.ExpressPackage)
                    .ToList();
            if (appliedGiftCards.Count > 0)
            {
                model.IsGiftCardApplied = true;
                model.GiftCardCode = appliedGiftCards.First().GiftCardCouponCode;
            }

            var giftCardApplied = false;
            if (creditCardId != 0)
            {
                var paymentMethod = _paymentService.LoadPaymentMethodBySystemName("Payments.GlobalPayments");
                var getPaymentCreditCard = paymentMethod.GetCreditCard(creditCardId);
                var paymentInfo = new SummaryModel.PaymentInfoType
                {
                    CardHolder = getPaymentCreditCard.CreditCardName,
                    CardNumber = getPaymentCreditCard.CreditCardNumber,
                    CardType = getPaymentCreditCard.CreditCardType,
                    ExpMonth = getPaymentCreditCard.CreditCardExpireMonth,
                    ExpYear = getPaymentCreditCard.CreditCardExpireYear,
                    Id = getPaymentCreditCard.Id
                };
                model.PaymentInfo = paymentInfo;
                _httpContext.Session[PaymentContants.PaymentInfoSessionKey] = new ProcessPaymentRequest()
                {
                    CreditCardNumber = getPaymentCreditCard.CreditCardNumber,
                    CreditCardType = getPaymentCreditCard.CreditCardType,
                    CreditCardExpireMonth = getPaymentCreditCard.CreditCardExpireMonth,
                    CreditCardExpireYear = getPaymentCreditCard.CreditCardExpireYear,
                    CreditCardName = getPaymentCreditCard.CreditCardName,
                    CreditCardId = getPaymentCreditCard.Id
                };
            }

            foreach (var li in oi.Locations)
            {
                var locModel = PrepareDetailViewModel(customer, li, oi.UserAddressId);

                if (li.Id == oi.CurrentLocationId)
                {
                    model.CurrentPackages = locModel.Packages;
                }
                if (appliedGiftCards.Count > 0 && locModel.Packages.Count > 0 && !giftCardApplied)
                {
                    // Has discount of first package
                    model.Discount += locModel.Packages[0].Charge;
                    giftCardApplied = true;
                }
                model.Locations.Add(locModel);

                model.TotalPackages += locModel.Packages.Count;
                model.SubTotalExcSurchargeTax += locModel.SubTotalExcSurchageTax;
            }
            // Discount cannot be larger than total payable
            if (model.Discount > model.SubTotalExcSurchargeTax)
                model.Discount = model.SubTotalExcSurchargeTax;

            var SubTotalInclDiscount = model.SubTotalExcSurchargeTax - model.Discount;

            model.Total = SubTotalInclDiscount;

            if (SubTotalInclDiscount > 0)
            {
                // Subtotal after discount is larger than 0, need to apply tax and fuel surcharge
                model.FuelSurchargeRate = _logisticsInformationSettings.FuelSurchargeRate;
                model.FuelSurcharge = SubTotalInclDiscount * model.FuelSurchargeRate;
                model.TaxRate = _logisticsInformationSettings.TaxRate;
                model.Tax = (model.FuelSurcharge + SubTotalInclDiscount) * _logisticsInformationSettings.TaxRate;
                model.Total = model.Total + model.Tax + model.FuelSurcharge;
            }

            var sa = customer.ShippingAddress;
            if (sa != null)
            {
                model.UserAddress = PrepareAddressModel(sa);
            }

            var ba = customer.BillingAddress;
            if (ba != null)
            {
                model.BillingAddress = PrepareAddressModel(ba);
            }

            return model;
        }

        [NonAction]
        protected LocationAddressModel PrepareLocationAddressModel(Customer customer, int? AddressId = 0, bool requiredAddrTypeVerified = false, LocationInformation li = null)
        {
            var model = new LocationAddressModel();
            if (li == null)
            {
                var oi = LoadOrderInformation(customer);
                li = LoadCurrentLocation(oi);
            }


            model.OrderByInfo = li != null && !String.IsNullOrEmpty(li.OrderInfo) ? li.OrderInfo : customer.GetCompanyNameOrFullName();
            model.HasInsurance = false;
            model.InsuranceValue = 0;

            if (li != null)
            {
                model.AdditionalInstruction = li.AddtionalInstruction;
                model.NoSignature = li.NoSignature;
                if (model.NoSignature == false && li.Packages != null && li.Packages.Any(p => p.InsuranceValue.HasValue))
                {
                    model.HasInsurance = true;
                    model.InsuranceValue = li.Packages.Sum(p => p.InsuranceValue.Value);
                }
            }

            if (model.NoSignature == null)
                model.NoSignature = false;

            model.Address = new AddressModel()
            {
                CountryId = AddressHelper.GetDefaultCountryId(),
                CountryName = AddressHelper.GetDefaultCountryName()
            };
            if (AddressId.HasValue)
            {
                if (AddressId.Value == -1 && customer.ShippingAddress != null)
                {
                    // Use user shipping address
                    model.Address = PrepareAddressModel(customer.ShippingAddress);
                    model.IsAddressTypeVerified = requiredAddrTypeVerified ? customer.ShippingAddress.IsAddressTypeVerified() : false;
                }
                else if (AddressId.Value > 0)
                {
                    var address = _addressService.GetAddressById(AddressId.Value);
                    if (address != null)
                        model.Address = PrepareAddressModel(address);
                    model.IsAddressTypeVerified = requiredAddrTypeVerified ? address.IsAddressTypeVerified() : false;
                }

                model.Address.IsTypeRequired = requiredAddrTypeVerified;
            }

            PrepareLocationAddressModelConst(customer, model);

            return model;
        }

        [NonAction]
        protected void PrepareLocationAddressModelConst(Customer customer, LocationAddressModel model, LocationInformation li = null)
        {
            var co = _countryService.GetCountryByTwoLetterIsoCode("CA");
            var states = _stateProvinceService.GetStateProvincesByCountryId(co.Id).Where(s => s.Name.Equals("British Columbia")).ToList();
            if (states.Count > 0)
            {
                model.AvailableStateProvinces = states
                    .Select(x => new SelectListItem() { Value = x.Id.ToString(), Text = x.Name, Selected = false })
                    .ToList();
                model.AvailableStateProvinces.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Logistics.Common.SelectProvince"), Value = "0", Selected = true });
            }

            var countries = _countryService.GetAllCountries();
            foreach (var c in countries)
            {
                var l = new SelectListItem()
                {
                    Value = c.Id.ToString(),
                    Text = c.Name
                };
                model.AvailableCountries.Add(l);
            }

            model.Addresses = customer.Addresses.Select(a =>
                new SelectListItem()
                {
                    Selected = model.Address != null && model.Address.Id == a.Id,
                    Text = String.IsNullOrEmpty(a.Company) ?
                        String.Format("{0} {1} <br />{2}", a.FirstName, a.LastName, a.FormattedAddress()) :
                        String.Format("{0} {1} ({2})<br />{3}", a.FirstName, a.LastName, a.Company, a.FormattedAddress()),

                    Value = a.Id.ToString()
                }).ToList();

            if (li == null)
            {
                var oi = LoadOrderInformation(customer);
                li = LoadCurrentLocation(oi);
            }

            //if customer has no address
            if (model.Address == null)
            {
                model.Address = new AddressModel();
            }

            PreparePickupTime(li, model);
        }

        [NonAction]
        protected ShippingOptionModel PrepareShippingOptionModel(Customer customer, LocationInformation li, ShippingOptionModel model)
        {
            if (model == null)
                model = new ShippingOptionModel();

            var address = _addressService.GetAddressById(li.AddressId);
            if (address != null && address.Type.HasValue && address.Type.Value == AddressType.Business)
            {
                model.CanSetSignatureRequirement = false;
                model.NoSignature = false;
            }
            else
            {
                model.CanSetSignatureRequirement = true;
                model.NoSignature = li.NoSignature;
            }
            model.AdditionalInstruction = li.AddtionalInstruction;
            model.OrderByInfo = li.OrderInfo ?? customer.GetCompanyNameOrFullName();
            model.HasInsurance = false;
            model.InsuranceValue = 0;
            if (model.NoSignature == false && li.Packages.Any(p => p.InsuranceValue.HasValue))
            {
                model.HasInsurance = true;
                model.InsuranceValue = li.Packages.Sum(p => p.InsuranceValue.Value);
            }
            model.IsGiftCardApplied = false;
            var giftCards = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer);
            var giftCard = giftCards.Where(gc => gc.GiftCardType == GiftCardType.ExpressPackage).FirstOrDefault();
            if (giftCard != null)
            {
                model.IsGiftCardApplied = true;
                model.GiftCardCode = giftCard.GiftCardCouponCode;
            }
            //PreparePickupTime(li, model);
            return model;
        }

        [NonAction]
        protected PaymentInfoModel PreparePaymentInfoModel(Customer customer, Charge charge = null, PaymentInfoModel model = null)
        {
            if (model == null)
                model = new PaymentInfoModel();

            if (charge == null)
                charge = _orderProcessingService.CaculateTotal(LoadOrderInformation(customer), customer);

            if (charge.SubTotalInclDiscount > 0 && !_orderProcessingService.IsPaymentExempt(customer))
                model.PaymentRequired = true;

            if (_logisticsInformationSettings.GpDebug)
                model.IsDebug = true;

            var c = _countryService.GetCountryByTwoLetterIsoCode("CA");
            var states = _stateProvinceService.GetStateProvincesByCountryId(c.Id).ToList();
            //var  paymentsCreditCard= _globalPaymentsService.GetCreditCardsByCustomer(customerId, storeId);
            if (states.Count > 0)
            {
                model.AvailableStateProvinces = states
                    .Select(x => new SelectListItem() { Value = x.Id.ToString(), Text = x.Name, Selected = false })
                    .ToList();
                model.AvailableStateProvinces.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Logistics.Common.SelectProvince"), Value = "0", Selected = true });
            }
            model.Addresses = customer.Addresses.Select(a =>
                new SelectListItem()
                {
                    Selected = false,
                    Text = String.IsNullOrEmpty(a.Company) ?
                        String.Format("{0} {1}, {2}", a.FirstName, a.LastName, a.FormattedAddress()) :
                        String.Format("{0} {1}({2}), {3}", a.FirstName, a.LastName, a.Company, a.FormattedAddress()),

                    Value = a.Id.ToString()
                }).ToList();

            model.BillingAddress = customer.BillingAddress != null ? PrepareAddressModel(customer.BillingAddress) : new AddressModel();
            //model.GetPaymentUrl = GetCardOnFileFormUrl();
            model.StoreId = GetStoreId();
            model.CustomerId = _workContext.CurrentCustomer.Id;
            return model;
        }

        [NonAction]
        protected OrderMenuModel PrepareOrderMenuModel(string viewname, LocationInformation li, OrderInformation oi, bool orderCompleted = false)
        {
            var model = new OrderMenuModel();
            model.IsOrderCompleted = orderCompleted;
            if (orderCompleted)
            {
                // Order completed, disable all menu steps
                model.Complete = OrderMenuItem.Current;
            }
            else
            {
                var customer = _workContext.CurrentCustomer;
                var userAddress = oi.UserAddressId.HasValue ? _addressService.GetAddressById(oi.UserAddressId.Value) : (customer.ShippingAddress != null ? customer.ShippingAddress : null);
                var address = li != null && li.AddressId > 0 ? _addressService.GetAddressById(li.AddressId) : null;

                // Pickup Address
                if (viewname != CommonValues.PickupAddressViewName)
                {
                    if (userAddress == null || String.IsNullOrEmpty(userAddress.ZipPostalCode))
                    {
                        model.PickupAddress = OrderMenuItem.Error;
                    }
                    else
                    {
                        model.PickupAddress = OrderMenuItem.Complete;
                    }
                }
                else
                {
                    model.PickupAddress = OrderMenuItem.Current;
                }

                if (li != null)
                {
                    // Delivery Address
                    if (viewname != CommonValues.DeliveryAddressViewName)
                    {
                        if (address == null || String.IsNullOrEmpty(address.ZipPostalCode))
                        {
                            model.DeliveryAddress = OrderMenuItem.Error;
                        }
                        else
                        {
                            model.DeliveryAddress = OrderMenuItem.Complete;
                        }
                    }
                    else
                    {
                        model.DeliveryAddress = OrderMenuItem.Current;
                    }
                }

                // Restrict access to other steps if addresses are interdistrict or either is missing
                if (userAddress != null && address != null && IsInterDistrict(PrepareAddressModel(userAddress), PrepareAddressModel(address)) ||
                    (userAddress == null || address == null))
                {
                    model.Packages = OrderMenuItem.Disabled;
                    model.ShippingOptions = OrderMenuItem.Disabled;
                    model.Billing = OrderMenuItem.Disabled;
                    model.Finalize = OrderMenuItem.Disabled;
                }
                // Can continue as both addresses are legit
                else
                {
                    if (oi.OrderCompleted || (model.PickupAddress != OrderMenuItem.Disabled && model.DeliveryAddress != OrderMenuItem.Disabled))
                    {
                        // Shipping Options
                        if (viewname != CommonValues.ShippingOptionViewName)
                        {
                            model.ShippingOptions = OrderMenuItem.Complete;
                        }
                        else
                        {
                            model.ShippingOptions = OrderMenuItem.Current;

                        }
                    }

                    // Packages
                    if (oi.OrderCompleted || (model.PickupAddress != OrderMenuItem.Disabled && model.DeliveryAddress != OrderMenuItem.Disabled && model.ShippingOptions != OrderMenuItem.Disabled))
                    {
                        if (viewname != CommonValues.PackageInfoViewName)
                        {

                            if (li.Packages.Count > 0 && li.Packages.Count >= li.CurrentPackageId)
                            {
                                model.Packages = OrderMenuItem.Complete;
                            }
                            else if (String.IsNullOrEmpty(li.OrderInfo) || viewname == CommonValues.ShippingOptionViewName && li.Packages.Count == 0)
                            {
                                model.Packages = OrderMenuItem.Disabled;
                            }
                            else
                            {
                                model.Packages = OrderMenuItem.Error;
                            }
                        }
                        else
                        {
                            model.Packages = OrderMenuItem.Current;
                        }
                    }

                    // Billing and payment

                    var pi = LoadPaymentInformation(customer);
                    var paymentExempt = _orderProcessingService.IsPaymentExempt(customer);
                    if (viewname != CommonValues.PaymentInfoViewName && viewname != CommonValues.BillingAddressViewName)
                    {
                        if (orderCompleted || (!orderCompleted && model.Packages != OrderMenuItem.Disabled && model.ShippingOptions != OrderMenuItem.Disabled))
                        {
                            if (customer.BillingAddress != null && (paymentExempt || (!paymentExempt && pi != null && pi.CreditCardId > 0)))
                            {
                                model.Billing = OrderMenuItem.Complete;
                            }
                            else if (pi == null || !(pi.CreditCardId > 0))
                            {
                                model.Billing = OrderMenuItem.Disabled;
                            }
                            else
                            {
                                model.Billing = OrderMenuItem.Error;
                            }
                        }
                    }
                    else
                    {
                        model.Billing = OrderMenuItem.Current;
                    }
                }

                if (viewname == CommonValues.FinalizeViewName)
                {
                    model.Finalize = OrderMenuItem.Current;
                }
                else if (oi.OrderCompleted)
                {
                    model.Finalize = OrderMenuItem.Complete;
                }
                else
                {
                    model.Finalize = OrderMenuItem.Disabled;
                }
            }
            return model;
        }

        [NonAction]
        protected void PrepareLocationModelConst(Customer customer, OrderLocationModel model)
        {
            model.StandardSize = _logisticsInformationSettings.RegularDimensionSize;
            model.MaxSize = _logisticsInformationSettings.MaxDimensionSize;
            model.StandardWeight = _logisticsInformationSettings.RegularWeight;
            model.MaxWeight = _logisticsInformationSettings.MaxWeight;
            model.OverSizeCharge = _logisticsInformationSettings.OverSizeCharge;
            model.OverWeightCharge = _logisticsInformationSettings.OverWeightCharge;
            model.MaxPackageNumber = _permissionService.Authorize(StandardPermissionProvider.LogisticsVolumeShipping) ?
                _logisticsInformationSettings.VolumeMaxPackagePerLocation :
                _logisticsInformationSettings.MaxPackagePerLocation;

            if (model.Address != null)
            {
                if (String.IsNullOrEmpty(model.Address.StateProvinceName))
                {
                    model.Address.StateProvinceName = AddressHelper.GetDefaultProvinceName();
                }
                if (String.IsNullOrEmpty(model.Address.CountryName))
                {
                    model.Address.CountryName = AddressHelper.GetDefaultCountryName();
                }
                if (String.IsNullOrEmpty(model.UserAddress.StateProvinceName))
                {
                    model.UserAddress.StateProvinceName = AddressHelper.GetDefaultProvinceName();
                }
                if (String.IsNullOrEmpty(model.UserAddress.CountryName))
                {
                    model.UserAddress.CountryName = AddressHelper.GetDefaultCountryName();
                }
            }

            model.Addresses = customer.Addresses.Select(a =>
                new SelectListItem()
                {
                    Selected = false,
                    Text = String.IsNullOrEmpty(a.Company) ?
                        String.Format("{0} {1}, {2}", a.FirstName, a.LastName, a.FormatAddressShort()) :
                        String.Format("{0} {1}({2}), {3}", a.FirstName, a.LastName, a.Company, a.FormatAddressShort()),
                    Value = a.Id.ToString()
                }).ToList();

            var regions = _logisticsService.GetAllRegions().Where(r => r.Name.Equals("Vancouver", StringComparison.OrdinalIgnoreCase));

            model.Regions = regions.Select(r =>
                new SelectListItem()
                {
                    Selected = model.SelectedRegionId == r.Id,
                    Text = r.GetLocalized(x => x.Name),
                    Value = r.Id.ToString()
                }).ToList();

            model.RegionStateProvinceMap = regions.Select(r =>
                new OrderLocationModel.RegionStateProvice()
                {
                    RegionId = r.Id,
                    StateProvinceId = AddressHelper.GetProvinceByRegion(r).Id,
                    StateProvinceName = AddressHelper.GetProvinceByRegion(r).GetLocalized(x => x.Name)
                }).ToList();

            //Get all published provinces in Canada
            //TODO: This is for Canada only at this point.
            var c = _countryService.GetCountryByTwoLetterIsoCode("CA");

            //states
            var states = _stateProvinceService.GetStateProvincesByCountryId(c.Id).ToList();
            if (states.Count > 0)
            {
                model.AvailableUserAddrStateProvinces = states
                    .Select(x => new SelectListItem() { Value = x.Id.ToString(), Text = x.Name, Selected = false })
                    .ToList();
                model.AvailableUserAddrStateProvinces.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Logistics.Common.SelectProvince"), Value = "0", Selected = true });

                model.AvailableAddressStateProvinces = states
                    .Select(x => new SelectListItem() { Value = x.Id.ToString(), Text = x.Name, Selected = false })
                    .ToList();
                model.AvailableAddressStateProvinces.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Logistics.Common.SelectProvince"), Value = "0", Selected = true });
            }
            else
            {
                throw new NopException("The province settings is incorrect.");
            }
        }

        [NonAction]
        protected OrderLocationModel PrepareLocationModel(Customer customer, LocationInformation li, int? userAddressId, bool resetPickupTime = false)
        {
            var model = new OrderLocationModel();

            if (li != null)
            {
                model.SelectedRegionId = li.RegionId;
            }
            else
            {
                model.SelectedRegionId = customer.Region == null ? _logisticsService.GetAllRegions().First().Id : customer.Region.Id;
            }

            PrepareLocationModelConst(customer, model);

            var userAddress = userAddressId.HasValue ? _addressService.GetAddressById(userAddressId.Value) : customer.ShippingAddress;
            var userAddressModel = PrepareAddressModel(userAddress);

            if (li == null)
            {
                model.Id = 0;
                model.IsExpress = true;
                model.IsUserAddressPickup = true;
                model.UserAddress = userAddressModel;
                model.IsUserAddressTypeVerified = userAddress != null ? userAddress.IsAddressTypeVerified() : false;

                model.Address = new AddressModel()
                {
                    CountryId = AddressHelper.GetDefaultCountryId(),
                    CountryName = AddressHelper.GetDefaultCountryName(),
                    IsTypeRequired = true
                };
                model.IsAddressTypeVerified = false;
                model.NoSignature = null;
                model.CanDeliverOnSat = false;
                model.CanDeliverOnSun = false;
            }
            else
            {
                var addr = customer.Addresses.SingleOrDefault(a => a.Id == li.AddressId);
                if (addr != null)
                {
                    model.Address = PrepareAddressModel(addr);
                    model.IsAddressTypeVerified = addr.IsAddressTypeVerified();
                    model.Address.IsTypeRequired = true;
                }
                model.Id = li.Id;
                model.IsExpress = false;
                model.IsUserAddressPickup = li.IsUserAddressPickup;
                model.UserAddress = userAddressModel;
                model.IsUserAddressTypeVerified = userAddress != null ? userAddress.IsAddressTypeVerified() : false;
                model.NoSignature = li.NoSignature;
                model.CanDeliverOnSat = false; // Disable Saturday delivery
                model.CanDeliverOnSun = false; // Disable Sunday delivery
                model.AdditionalInstruction = li.AddtionalInstruction;
                model.PickupDate = li.PickupDate;
                model.PickupTimeWindowId = li.PickupTimeWindowId;
            }

            model.HasInsuranceOnPackages = false;
            if (model.NoSignature == false && li.Packages.Any(p => p.InsuranceValue.HasValue))
            {
                model.HasInsuranceOnPackages = true;
            }

            model.ResetPickupTime = false;
            if (resetPickupTime && li != null && li.PickupDate.HasValue && li.PickupTimeWindowId.HasValue)
            {
                li.PickupDate = null;
                li.PickupTimeWindowId = null;
                model.ResetPickupTime = true;
            }

            //PreparePickupTime(li, model);

            model.UserAddress.IsTypeRequired = true;

            return model;
        }

        [NonAction]
        protected bool IsInterDistrict(AddressModel address1, AddressModel address2)
        {
            var district1 = _logisticsService.GetDistrictByZipPostalCode(address1.ZipPostalCode);
            var district2 = _logisticsService.GetDistrictByZipPostalCode(address2.ZipPostalCode);

            return (district1 != null && district2 != null && district1.Id != district2.Id);
        }

        [NonAction]
        protected bool IsResidential(AddressType deliveryAddrType, bool noSignature)
        {
            return (deliveryAddrType == AddressType.Residential && !noSignature);
        }

        [NonAction]
        protected bool InsuranceEnabled(LocationInformation li)
        {
            return !(li.NoSignature.HasValue && li.NoSignature.Value);
        }

        [NonAction]
        protected OrderPackageModel PreparePackageModel(PackageInformation pi, IEnumerable<PackageInformation> otherPackages)
        {
            var model = new OrderPackageModel();
            model.MaxDimensionSize = _logisticsInformationSettings.MaxDimensionSize;
            model.MaxSingleDimensionSize = _logisticsInformationSettings.SingleMaxDimensionSize;
            model.MaxWeight = _logisticsInformationSettings.MaxWeight;
            model.FragileCharge = _logisticsInformationSettings.FragileCharge;
            model.DangerousGoodsCharge = _logisticsInformationSettings.DangerousGoodCharge;
            model.VerticalHandlingCharge = _logisticsInformationSettings.VerticalHandleCharge;
            model.NoLabelCharge = 1M;
            model.MaxCommentLength = _logisticsInformationSettings.MaxCommentLength;
            model.ChargeRule = _orderProcessingService.GetChargeRule(_workContext.CurrentCustomer);
            model.CurrentTotalWeight = otherPackages == null ? 0M : otherPackages.Sum(p => p.Weight ?? 0M);
            model.CurrentTotalAddtionalCharge = otherPackages == null ? 0M : otherPackages.Sum(p => AddtionalChargeHelper.GetAddtionalCharge(p));
            model.CurrentOversizeCount = otherPackages == null ? 0 : otherPackages.Count(p => p.GetTotalDim() > _logisticsInformationSettings.OversizeDimensionSize);
            model.PackageCount = otherPackages == null ? 0 : otherPackages.Count();
            model.CanAddAnotherPackage = (otherPackages != null && otherPackages.Count() > 0) || pi != null;
            model.DimensionMeasureId = (pi == null || !pi.DimensionMeasureId.HasValue) ? _measureSettings.BaseDimensionId : pi.DimensionMeasureId.Value;
            foreach (var d in _measureService.GetAllMeasureDimensions())
            {
                model.AvaliableDimensionMeasure.Add(new AvailableMeasure()
                {
                    Text = d.Name,
                    Value = d.Id.ToString(),
                    Selected = model.DimensionMeasureId == d.Id,
                    ConversionRatio = d.Ratio
                });
            }

            model.WeightMeasureId = (pi == null || !pi.WeightMeasureId.HasValue) ? _measureSettings.BaseWeightId : pi.WeightMeasureId.Value;
            foreach (var w in _measureService.GetAllMeasureWeights())
            {
                model.AvaliableWeightMeasure.Add(new AvailableMeasure()
                {
                    Text = w.Name,
                    Value = w.Id.ToString(),
                    Selected = model.WeightMeasureId == w.Id,
                    ConversionRatio = w.Ratio
                });
            }

            if (pi != null)
            {
                model.PackageId = pi.Id;
                model.IsVertical = pi.AddtionalAttributes.HasFlag(AddtionalAttributes.VerticalHandle);
                model.IsFragile = pi.AddtionalAttributes.HasFlag(AddtionalAttributes.Fragile);
                model.IsDangerous = pi.AddtionalAttributes.HasFlag(AddtionalAttributes.DangerousGood);
                model.IsNoLabel = pi.AddtionalAttributes.HasFlag(AddtionalAttributes.NoLabel);
                model.InsuranceValue = pi.InsuranceValue;
                model.OrderKindId = pi.OrderKindId;
                //model.IsLettersize = pi.IsLettersize;
                model.PackagePreset = pi.PackagePreset;
                model.Weight = pi.Weight;
                model.Length = pi.Length;
                model.Width = pi.Width;
                model.Height = pi.Height;
                model.Comment = pi.Comment;
            }
            else
            {
                model.PackageId = 0;
                model.IsFragile = false;
                model.IsDangerous = false;
                model.IsVertical = false;
                model.IsNoLabel = otherPackages == null || otherPackages.Count() == 0 ||
                    otherPackages.First().AddtionalAttributes.HasFlag(AddtionalAttributes.NoLabel);
                //model.IsLettersize = false;
                model.PackagePreset = null;
                model.Weight = null;
                model.Length = null;
                model.Width = null;
                model.Height = null;
                model.Comment = null;
            }
            if (!model.PackagePreset.HasValue)
                model.PackagePreset = (int)PackagePreset.None;
            return model;
        }

        [NonAction]
        protected SummaryModel.LocationModel PrepareDetailViewModel(Customer customer, LocationInformation li, int? userAddressId)
        {
            var model = new SummaryModel.LocationModel();

            model.UserAddress = PrepareAddressModel(userAddressId.HasValue ?
                _addressService.GetAddressById(userAddressId.Value) : customer.ShippingAddress);

            model.IsUserAddressPickup = li.IsUserAddressPickup;
            var address = customer.Addresses.SingleOrDefault(a => a.Id == li.AddressId);
            if (address != null)
            {
                model.Address = PrepareAddressModel(address);
                //throw new NopException("The address ({0}) doesn't exist when generating summary. Customer ID: {1}.",
                //    li.AddressId,
                //    customer.Id);
            }


            //var pickTimeStr = _logisticsService.GetPickUpTimeFromGenericAttribute(OrderKind.Express);


            //add this value to show the time user select 
            if (li != null && li.PickupDate != null && li.PickupTimeWindowId != null)
            {
                model.PickupTimeUserSelected = li.PickupDate.Value.ToString("MM/dd/yyyy") + " " + _logisticsService.GetShippingTimeWindowById(li.PickupTimeWindowId.Value).Description;
            }

            if (li != null && li.PickupDate != null && li.PickupTimeWindowId != null)
            //if(!string.IsNullOrEmpty(li.PickupDate))
            {
                //var pickupAddress = li.IsUserAddressPickup ? customer.Addresses.SingleOrDefault(a => a.Id == model.UserAddress.Id) : customer.Addresses.SingleOrDefault(a => a.Id == model.UserAddress.Id);
                //string warning=string.Empty;

                //var pickTimeWindowAttr = pickTimeStr.Split(';');
                //model.PickupTimeStr = _logisticsService.GetShowPickUpTime(pickTimeWindowAttr[0], int.Parse(pickTimeWindowAttr[1]));
                model.PickupTimeStr = _logisticsService.GetShowPickUpTime(li.PickupDate.Value, li.PickupTimeWindowId.Value, new Order() { OrderKindId = (int)OrderKind.Express, ShippingAddressId = li.AddressId, });

                //model.PickupTimeStr = _orderProcessingService.GetValidatedPickupTime(li, OrderKind.Express, pickupAddress.ZipPostalCode, pickupAddress.VerifiedType.HasValue ? pickupAddress.VerifiedType.Value : pickupAddress.Type.Value, out warning);
                //if (!String.IsNullOrEmpty(warning))
                //{
                //    var oi = LoadOrderInformation(customer);
                //    li.PickupTimeWindowId = null;
                //    li.PickupDate = null;
                //    _orderProcessingService.SaveOrderInformation(oi, customer);
                //    AddNotification(Nop.Web.Framework.UI.NotifyType.Warning, warning, true);
                //}

            }

            model.AdditionalInstruction = li.AddtionalInstruction;

            if (li.Packages.Count == 0)
            {
                model.SubTotalExcSurchageTax = 0;
                model.FuelSurcharge = 0;
                model.Tax = 0;
                model.SubTotal = 0;
                return model;
            }

            var charge = _orderProcessingService.CaculateTotal(li, customer);

            model.IsGiftCardApplied = false;
            if (charge.SubTotalExclTax != charge.SubTotalInclDiscount)
                model.IsGiftCardApplied = true;

            if (charge.Warnings.Count > 0)
            {
                var errMsg = "Caculate total error:" + String.Join(";", charge.Warnings);
                LogCustomerActivity(errMsg, customer, _customerActivityService);
                throw new NopException(errMsg);
            }

            for (int i = 0; i < li.Packages.Count; i++)
            {
                var p = li.Packages[i];
                var pkg = new SummaryModel.PackageModel();
                pkg.Id = p.Id;
                pkg.Name = _orderProcessingService.GetPackageInfoString(p);
                pkg.Comment = p.Comment;
                pkg.Attributes = (p.AddtionalAttributes & ~AddtionalAttributes.NoLabel).GetLocalizedEnum(_localizationService, _workContext);
                pkg.Charge = charge.PackageCharges[i] + charge.AddtionalCharges[i];
                pkg.Insurance = pkg.Charge == 0 ? 0 : p.InsuranceValue;
                pkg.PackagePreset = p.PackagePreset;
                model.Packages.Add(pkg);
            }

            model.SubTotalExcSurchageTax = charge.SubTotalExclTax;
            model.FuelSurcharge = charge.FuelSurchargeExclTax;
            model.Tax = charge.Tax;
            model.SubTotal = charge.Total;

            model.OrderByInfo = li.OrderInfo ?? customer.GetCompanyNameOrFullName();

            return model;
        }

        [NonAction]
        protected List<PackageModel> PrepareDownloadModel()
        {
            var oi = LoadOrderInformation(_workContext.CurrentCustomer);
            var pkgs = new List<LogisticsPackage>();
            oi.Locations.ForEach(l => pkgs.AddRange(
                l.Packages.Where(p => p.PackageId.HasValue)
                               .Select(p => _logisticsService.GetPackageById(p.PackageId.Value))
                ));
            return pkgs.Select(PreparePackageModelForList).ToList();
        }

        #endregion

        #region Order Information

        private OrderInformation LoadOrderInformation(Customer customer)
        {
            return _orderProcessingService.LoadOrderInformation(customer);
        }

        private LocationInformation LoadCurrentLocation(OrderInformation oi)
        {
            return oi != null ? oi.Locations.SingleOrDefault(l => l.Id == oi.CurrentLocationId) : null;
        }

        private ProcessPaymentRequest LoadPaymentInformation(Customer customer)
        {
            return _orderProcessingService.LoadPaymentInformation(customer);
        }

        private OrderInformation InitOrder(Customer customer)
        {
            if (customer == null)
            {
                customer = _workContext.CurrentCustomer;
            }
            // Add new address into customer
            // Save address id and is-pickup into the attribute
            if (_orderProcessingService.HasPendingOrders(customer))
            {
                throw new NopException("InitOrder failed. Order information is not empty.");
            }

            // Remove any SpotsHUB Express giftcard if applicable
            var giftCards = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer).Where(gc => gc.GiftCardType == GiftCardType.ExpressPackage);
            if (giftCards.Count() > 0)
            {
                foreach (var giftCard in giftCards)
                {
                    customer.RemoveGiftCardCouponCode(giftCard.GiftCardCouponCode);
                }
                _customerService.UpdateCustomer(customer);
            }

            var oi = new OrderInformation()
            {
                OrderCompleted = false,
                OrderPaid = false,
                OrderId = null,
                CurrentLocationId = 0
            };

            _orderProcessingService.SaveOrderInformation(oi, customer);
            return oi;
        }

        private Address AddAddress(AddressModel model, Customer customer, bool addressTypeRequired = true)
        {
            if (addressTypeRequired && !model.Type.HasValue)
                ModelState.AddModelError("", "AddressModel type must not be null.");

            var address = customer.Addresses.ToList().FindAddress(
                        model.FirstName, model.LastName,
                        model.PhoneNumber, model.CellPhoneNumber,
                        model.Email, model.FaxNumber, model.Company,
                        model.Address1, model.Address2, model.City,
                        model.StateProvinceId, model.ZipPostalCode, model.CountryId);

            if (address == null)
            {
                address = new Address();
                AddressModelToAddress(model, address);
                address.CreatedOnUtc = DateTime.UtcNow;
                customer.Addresses.Add(address);
                _customerService.UpdateCustomer(customer);
            }
            else if (address.Type != model.Type)
            {
                if (address.IsAddressTypeVerified())
                {
                    model.Type = address.Type;
                }
                else if (model.Type.HasValue && model.Type.Value != AddressType.Unknown)
                {
                    address.Type = model.Type;
                    _addressService.UpdateAddress(address);
                }
            }

            return address;
        }

        private OrderInformation AddLocation(OrderLocationModel location, Customer customer, int userAddressId)
        {
            var oi = LoadOrderInformation(customer);
            oi.UserAddressId = userAddressId;
            var addressId = 0;
            if (!String.IsNullOrEmpty(location.Address.Address1) && !String.IsNullOrEmpty(location.Address.ZipPostalCode))
            {
                var address = AddAddress(location.Address, customer);
                addressId = address.Id;
            }

            oi.CurrentLocationId++;
            var li = new LocationInformation()
            {
                RegionId = location.SelectedRegionId,
                Id = oi.CurrentLocationId,
                AddressId = addressId,
                IsUserAddressPickup = location.IsUserAddressPickup,
                PickupDate = location.PickupDate,
                PickupTimeWindowId = location.PickupTimeWindowId,
                NoSignature = location.NoSignature.HasValue ? location.NoSignature : false,
                IsInterDistrict = false,
                IsResidential = false,
                CanDeliverOnSat = false,
                CanDeliverOnSun = false, // Disable Sunday delivery
                AddtionalInstruction = location.AdditionalInstruction
            };
            if (!String.IsNullOrEmpty(location.UserAddress.Address1) && !String.IsNullOrEmpty(location.UserAddress.ZipPostalCode))
                li.IsInterDistrict = IsInterDistrict(location.UserAddress, location.Address); // Update inter-region attribute
            if (location.Address.Type.HasValue && location.NoSignature.HasValue)
                li.IsResidential = IsResidential(location.Address.Type.Value, location.NoSignature.Value);

            oi.Locations.Add(li);
            oi.OrderCompleted = false;
            _orderProcessingService.SaveOrderInformation(oi, customer);
            return oi;
        }

        private OrderInformation UpdateLocation(OrderLocationModel location, Customer customer, int userAddressId, bool skipAddress = false)
        {
            if (location.UserAddress != null && !location.UserAddress.Type.HasValue)
                throw new NopException("The UserAddress type is null.");

            if (location.Address != null && !location.Address.Type.HasValue)
                throw new NopException("The Address type is null.");

            var oi = LoadOrderInformation(customer);
            oi.UserAddressId = userAddressId;

            var li = oi.Locations
                .Where(l => l.Id == oi.CurrentLocationId)
                .First();
            //var address = customer.Addresses
            //    .Where(a => a.Id == li.AddressId)
            //    .First();

            li.IsUserAddressPickup = location.IsUserAddressPickup;
            li.PickupDate = location.PickupDate;
            li.PickupTimeWindowId = location.PickupTimeWindowId;

            if (!skipAddress)
            {
                var address = new Address();
                if (location.Address != null)
                {
                    address = AddAddress(location.Address, customer);
                    li.AddressId = address.Id;
                }
            }

            li.RegionId = location.SelectedRegionId;

            li.AddtionalInstruction = location.AdditionalInstruction;

            // Update inter-region attribute
            li.IsInterDistrict = location.UserAddress != null && location.Address != null && IsInterDistrict(location.UserAddress, location.Address);

            if (!location.NoSignature.HasValue)
            {
                location.NoSignature = false;
            }

            //Update address type info
            li.IsResidential = location.Address != null && IsResidential(location.Address.Type.Value, location.NoSignature.Value);
            //Update the NoSignature of location info
            li.NoSignature = location.NoSignature;

            if (location.NoSignature.Value)
            {
                li.Packages.ForEach(p => p.AddtionalAttributes |= AddtionalAttributes.NoSignature);

                if (li.Packages.Any(p => p.InsuranceValue.HasValue))
                {
                    li.Packages.ForEach(p => p.AddtionalAttributes &= ~AddtionalAttributes.Insurance);
                    li.Packages.ForEach(p => p.InsuranceValue = null);
                }
            }
            else
            {
                li.Packages.ForEach(p => p.AddtionalAttributes &= ~AddtionalAttributes.NoSignature);
            }

            if (li.IsInterDistrict)
            {
                li.Packages.ForEach(p => p.AddtionalAttributes |= AddtionalAttributes.InterDistrict);
            }
            else
            {
                li.Packages.ForEach(p => p.AddtionalAttributes &= ~AddtionalAttributes.InterDistrict);
            }

            if (li.IsResidential)
            {
                li.Packages.ForEach(p => p.AddtionalAttributes |= AddtionalAttributes.Residential);
            }
            else
            {
                li.Packages.ForEach(p => p.AddtionalAttributes &= ~AddtionalAttributes.Residential);
            }

            _orderProcessingService.SaveOrderInformation(oi, customer);

            return oi;
        }

        private LocationInformation AddPackage(OrderPackageModel package, Customer customer)
        {
            var oi = LoadOrderInformation(customer);

            var li = oi.Locations
                .Single(l => l.Id == oi.CurrentLocationId);

            var pi = new PackageInformation()
            {
                Id = package.PackageId,
                AddtionalAttributes = AddtionalAttributes.None,
                Length = package.Length,
                Width = package.Width,
                Height = package.Height,
                Weight = package.Weight,
                InsuranceValue = package.InsuranceValue,
                Comment = package.Comment,
                DimensionMeasureId = package.DimensionMeasureId,
                WeightMeasureId = package.WeightMeasureId,
                PackagePreset = package.PackagePreset,
                OrderKindId = package.OrderKindId
            };

            if (package.IsVertical)
            {
                pi.AddtionalAttributes |= AddtionalAttributes.VerticalHandle;
            }

            if (package.IsFragile)
            {
                pi.AddtionalAttributes |= AddtionalAttributes.Fragile;
            }

            if (package.IsDangerous)
            {
                pi.AddtionalAttributes |= AddtionalAttributes.DangerousGood;
            }

            if (li.IsInterDistrict)
            {
                pi.AddtionalAttributes |= AddtionalAttributes.InterDistrict;
            }

            if (!li.NoSignature.HasValue)
            {
                li.NoSignature = true;
                LogCustomerActivity("Added signature attribute.", customer, _customerActivityService);
            }

            if (li.NoSignature.Value)
            {
                pi.AddtionalAttributes |= AddtionalAttributes.NoSignature;
                package.InsuranceEnabled = false;
            }
            else
            {
                package.InsuranceEnabled = true;
            }

            if (package.InsuranceEnabled)
            {
                if (package.InsuranceValue.HasValue && package.InsuranceValue.Value > _logisticsInformationSettings.InsuranceChargeStep)
                {
                    pi.AddtionalAttributes |= AddtionalAttributes.Insurance;
                }
            }

            if (li.IsResidential)
            {
                pi.AddtionalAttributes |= AddtionalAttributes.Residential;
            }

            li.Packages.Add(pi);

            _orderProcessingService.SaveOrderInformation(oi, customer);
            return li;
        }

        private LocationInformation UpdatePackage(OrderPackageModel package, Customer customer)
        {
            var oi = LoadOrderInformation(customer);

            var li = oi.Locations
                .Single(l => l.Id == oi.CurrentLocationId);

            if (!li.Packages.Exists(p => p.Id == package.PackageId))
            {
                throw new NopException("The package #{0} doesn't exist.");
            }

            var pkg = li.Packages
                .Single(p => p.Id == package.PackageId);

            bool hasPackageSize = true;
            pkg.PackagePreset = package.PackagePreset;

            if (!hasPackageSize) //(!package.IsLettersize)
            {
                pkg.Length = package.Length;
                pkg.Width = package.Width;
                pkg.Height = package.Height;
                pkg.Weight = package.Weight;
            }
            else
            {
                pkg.Length = null;
                pkg.Width = null;
                pkg.Height = null;
                pkg.Weight = null;
            }
            pkg.DimensionMeasureId = package.DimensionMeasureId;
            pkg.WeightMeasureId = package.WeightMeasureId;

            pkg.Comment = package.Comment;

            if (package.IsVertical)
            {
                pkg.AddtionalAttributes |= AddtionalAttributes.VerticalHandle;
            }
            else
            {
                pkg.AddtionalAttributes &= ~AddtionalAttributes.VerticalHandle;
            }

            if (package.IsFragile)
            {
                pkg.AddtionalAttributes |= AddtionalAttributes.Fragile;
            }
            else
            {
                pkg.AddtionalAttributes &= ~AddtionalAttributes.Fragile;
            }

            if (package.IsDangerous)
            {
                pkg.AddtionalAttributes |= AddtionalAttributes.DangerousGood;
            }
            else
            {
                pkg.AddtionalAttributes &= ~AddtionalAttributes.DangerousGood;
            }

            if (!li.NoSignature.HasValue || !li.NoSignature.Value)
            {
                package.InsuranceEnabled = true;
            }
            else
            {
                package.InsuranceEnabled = false;
            }

            if (package.InsuranceEnabled)
            {
                pkg.InsuranceValue = package.InsuranceValue;

                if (package.InsuranceValue.HasValue && package.InsuranceValue.Value > _logisticsInformationSettings.InsuranceChargeStep)
                {
                    pkg.AddtionalAttributes |= AddtionalAttributes.Insurance;
                }
                else
                {
                    pkg.AddtionalAttributes &= ~AddtionalAttributes.Insurance;
                }
            }
            else
            {
                pkg.InsuranceValue = null;
                pkg.AddtionalAttributes &= ~AddtionalAttributes.Insurance;
            }

            _orderProcessingService.SaveOrderInformation(oi, customer);
            return li;
        }

        private decimal CalculatePackageCost(LocationInformation locationInfo, Customer customer, int packageId)
        {
            packageId = packageId - 1;
            if (packageId > locationInfo.Packages.Count || packageId < 0)
            {
                throw new ArgumentException("The index is invalid.", "packageId");
            }
            var c = _orderProcessingService.CaculateTotal(locationInfo, customer);

            if (c.Warnings.Count > 0)
            {
                LogCustomerActivity("Caculate total error:" + String.Join(";", c.Warnings), customer, _customerActivityService);
                return 0;
            }

            if (_orderProcessingService.GetChargeRule(customer) == ChargeRule.Volume)
            {
                return c.AddtionalCharges.Sum() + c.PackageCharges.Sum();
            }
            if (c.AddtionalCharges.Count - 1 >= packageId && c.AddtionalCharges[packageId] >= 0 && c.PackageCharges.Count - 1 >= packageId && c.PackageCharges[packageId] >= 0)
                return c.AddtionalCharges[packageId] + c.PackageCharges[packageId];

            return 0;
        }

        #endregion

        #region Methods

        #region My Hub / User Profile

        [NopHttpsRequirement(SslRequirement.Yes)]
        public ActionResult PlaceOrder()
        {
            var customer = _workContext.CurrentCustomer;

            if (!customer.IsRegistered())
            {
                return AccessDeniedView();
            }
            /*
            bool hasPendingOrders = _orderProcessingService.HasPendingOrders(customer);
            if (hasPendingOrders)
            {
                _orderProcessingService.RemoveCurrentOrder(customer);
            }
            */
            return RedirectToAction("OrderInfo");
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        public ActionResult Index()
        {
            var customer = _workContext.CurrentCustomer;

            return View();
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        public ActionResult OrderList()
        {
            var customer = _workContext.CurrentCustomer;

            if (!customer.IsRegistered())
            {
                return AccessDeniedView();
            }
            return View();
        }

        [HttpPost]
        public ActionResult OrderList(OrderListModel model)
        {
            var customer = _workContext.CurrentCustomer;
            ModelState.Clear();
            if (customer != null)
            {
                var orders = _orderService.GetOrdersByCustomerId(customer.Id, 1, OrderKind.Express, model.Page - 1, model.PageSize);
                if (orders == null)
                {
                    ModelState.AddModelError("", "Unable to find shipping orders.");
                }
                model = new OrderListModel();
                model.Orders = orders;
                var rows = "";
                var hiddenrows = "";
                if (orders.Count > 0)
                {
                    foreach (var order in orders)
                    {
                        rows += "<tr class='order-list-row' data-id='" + order.Id + "'><td>" + order.Id + "</td><td>" + order.CreatedOnUtc.ToShortDateString() + " " + order.CreatedOnUtc.ToShortTimeString() + "</td><td>" + order.ShippingStatus + "</td><td>" + order.Shipments.Count + "</td></tr>";
                        hiddenrows += this.RenderPartialViewToString("OrderListShipments", order);
                    }
                }
                return Json(new
                {
                    data = rows,
                    hiddendata = hiddenrows + "<input type='hidden' id='data-page' name='Page' value='" + (model.Page - 1) + "' />",
                    pagerdata = RenderPager(orders.PageIndex, orders.TotalPages, orders.TotalCount, orders.HasPreviousPage, orders.HasNextPage)
                });
            }

            return null;
        }

        public JsonResult OrderCancel(int orderId)
        {
            var order = _orderService.GetOrderById(orderId);
            if (order == null)
            {
                return Json(new { isCancel = false, errorMesage = "order null delete error!" });
            }

            if (order.ShippingStatus != ShippingStatus.ReadyForPickUp)
            {
                return Json(new { isCancel = false, errorMesage = "Status not to delete failed!" });
            }
            _orderService.DeleteOrder(order);
            return Json(new { isCancel = true });
        }

        [HttpPost]
        public ActionResult GetAddress(int id)
        {
            //var order = _orderService.GetOrderById(id);
            //if (order == null)
            //{
            //    return Json(new { isGet = false });
            //}
            //var address = order.ShippingAddress;
            //if (address == null)
            //{
            //    return Json(new { isGet = false });
            //}
            var address = _workContext.CurrentCustomer.Addresses.FirstOrDefault(a => a.Id == id);
            if (address == null)
            {
                return Json(new { isGet = false });
            }
            var editAddress = new
            {
                Id = address.Id,
                FirstName = address.FirstName,
                LastName = address.LastName,
                Company = address.Company,
                Address1 = address.Address1,
                Address2 = address.Address2,
                City = address.City,
                StateProvinceId = address.StateProvince == null ? "" : address.StateProvince.Name,
                CountryName = address.Country == null ? "" : address.Country.Name,
                CountryId = address.CountryId,
                ZipPostalCode = address.ZipPostalCode,
                Email = address.Email,
                CellPhoneNumber = address.CellPhoneNumber,
                PhoneNumber = address.PhoneNumber,
            };
            return Json(new { isGet = true, address = editAddress });
        }

        [HttpPost]
        public JsonResult GetAddressList(int orderId)
        {
            var customer = _workContext.CurrentCustomer;
            var order = _orderService.GetOrderById(orderId);
            var selectAddressId = order.ShippingAddress.Id;
            if (customer != null && customer.Addresses != null)
            {

                var testList = from ad in customer.Addresses
                               select new
                               {
                                   AddressId = ad.Id,
                                   CountryId = ad.CountryId,
                                   CountryName = ad.Country.Name,
                                   FirstName = ad.FirstName,
                                   LastName = ad.LastName,
                                   Company = ad.Company,
                                   Address1 = ad.Address1,
                                   Address2 = ad.Address2,
                                   CellPhoneNumber = ad.CellPhoneNumber,
                                   TypeId = ad.TypeId,
                                   selectAddressId = selectAddressId
                               };
                testList = testList.ToList();
                if (testList.Any())
                {
                    return Json(new { success = true, addressList = testList });
                }
            }
            return Json(new { success = false });
        }

        [NonAction]
        public string RenderPager(int pageIndex, int totalPages, int totalCount, bool hasPrev, bool hasNext)
        {
            var pager = "";
            pageIndex += 1;
            if (totalPages > 1)
            {
                pager += "<ul class='pagination'>";
                pager += "<li class='" + (hasPrev ? "" : "disabled") + " prev'><a href='#' data-page='prev' aria-label='Previous'><span aria-hidden='true'>&laquo;</span></a></li>";
                var pages = new List<int>();
                if (totalPages <= 10)
                {
                    for (var i = 1; i <= 10; i++)
                    {
                        if (i <= totalPages)
                        {
                            pages.Add(i);
                        }
                    }
                }
                else
                {
                    pages.Add(pageIndex);
                    var decMax = 4;
                    for (var i = 1; i < 10; i++)
                    {
                        if (pageIndex - i > 0 && decMax > 0)
                        {
                            pages.Add(pageIndex - i);
                            decMax -= 1;
                        }
                        else
                        {
                            var incDif = 0;
                            if ((pageIndex + i - (4 - decMax)) > totalPages)
                            {
                                pages.Add(pageIndex - i + incDif);
                            }
                            else
                            {
                                pages.Add(pageIndex + i - (4 - decMax));
                                incDif += 1;
                            }
                        }
                    }
                    pages.Sort();
                }
                foreach (var page in pages)
                {
                    pager += "<li class='" + (page == pageIndex ? "active" : "") + " page'><a href='#' data-page='" + page + "'>" + page + "</a></li>";
                }
                pager += "<li class='" + (hasNext ? "" : "disabled") + " next'><a href='#'  data-page='next' aria-label='Next'><span aria-hidden='true'>&raquo;</span></a></li>";
                pager += "</ul>";
            }
            return pager;
        }

        public ActionResult PendingOrder(string type)
        {
            var customer = _workContext.CurrentCustomer;
            if (!String.IsNullOrEmpty(type))
            {
                if (type == "new")
                {
                    return RemovePendingOrder();
                }

                return InitView("", true);
            }
            return PartialView();
        }
        [NopHttpsRequirement(SslRequirement.Yes)]
        [HttpPost]
        public ActionResult RemovePendingOrder()
        {
            var customer = _workContext.CurrentCustomer;
            var oi = LoadOrderInformation(customer);
            if (_orderProcessingService.HasPendingOrders(customer) || oi != null)
            {
                if (oi != null && oi.OrderId.HasValue && oi.OrderId.Value > 0)
                {
                    // Void a placed order, if any
                    var order = _orderService.GetOrderById(oi.OrderId.Value);
                    _orderService.DeleteOrder(order);
                }
                _orderProcessingService.RemoveCurrentOrder(customer, true);
                _orderProcessingService.RemovePaymentInformation(customer);
            }
            return InitView("");
        }

        [HttpPost]
        public ActionResult PickupAddress(LocationAddressModel model, bool selectAddress = false)
        {
            var customer = _workContext.CurrentCustomer;
            var notices = new List<string>();
            var warnings = new List<string>();
            try
            {
                var oi = LoadOrderInformation(customer);
                if (oi == null)
                    oi = InitOrder(customer);

                var li = oi.Locations.SingleOrDefault(l => l.Id == oi.CurrentLocationId);
                var locmodel = new OrderLocationModel();
                if (li == null)
                {
                    locmodel = PrepareLocationModel(customer, null, oi.UserAddressId);
                }
                else
                {
                    locmodel = PrepareLocationModel(customer, li, oi.UserAddressId);
                }

                if (selectAddress)
                {
                    ModelState.Clear();
                    if (model.SelectedAddressId.HasValue && model.SelectedAddressId != 0)
                    {
                        var addr = customer.Addresses.FirstOrDefault(a => a.Id == model.SelectedAddressId);
                        if (addr != null)
                        {
                            model.Address = PrepareAddressModel(addr);
                            model.Address.IsTypeRequired = true;
                            model.IsAddressTypeVerified = addr.IsAddressTypeVerified();
                            notices.Add("Pickup address selected.");
                        }
                        else
                        {
                            ModelState.AddModelError("", "Selected address does not exist.");
                        }
                        PrepareLocationAddressModelConst(customer, model);
                    }
                    return Json(GetJsonView(CommonValues.PickupAddressViewName, model, notices));
                }

                // Check inter-district
                if (model.Address != null && locmodel.Address != null && !String.IsNullOrEmpty(model.Address.ZipPostalCode) && !String.IsNullOrEmpty(locmodel.Address.ZipPostalCode))
                {
                    var district1 = _logisticsService.GetDistrictByZipPostalCode(model.Address.ZipPostalCode);
                    var district2 = _logisticsService.GetDistrictByZipPostalCode(locmodel.Address.ZipPostalCode);
                    if (district1 != null && district2 != null && district1.Id != district2.Id)
                    {
                        ModelState.AddModelError("", "Inter-district shipping between " + district1.Name + " and " + district2.Name + " is currently not allowed.");
                        PrepareLocationAddressModelConst(customer, model);
                        return Json(GetJsonView(CommonValues.PickupAddressViewName, model));
                    }
                }

                // Check address type
                if (!model.Address.Type.HasValue)
                    ModelState.AddModelError("", "Address type must be selected.");

                // Pickup time preference
                if (model.HasPickupDate)
                {
                    if (!model.PickupDate.HasValue || !model.PickupTimeWindowId.HasValue)
                        ModelState.AddModelError("", "Must select a pickup time.");

                    locmodel.PickupTimeWindowId = model.PickupTimeWindowId;
                    locmodel.PickupDate = model.PickupDate;
                }
                else
                {
                    if (model.Address != null && model.Address.Type.HasValue)
                    {
                        var pickUpModel = _logisticsService.GetPickerModel((int)OrderKind.Express, model.Address.Type.Value, model.Address.ZipPostalCode);
                        if (pickUpModel != null)
                        {
                            locmodel.PickupTimeWindowId = model.PickupTimeWindowId;
                            locmodel.PickupDate = model.PickupDate;
                        }
                    }
                }

                if (ModelState.IsValid)
                {
                    // Remove spaces in the postal code.
                    model.Address.ZipPostalCode = model.Address.ZipPostalCode.Replace(" ", string.Empty).Trim().ToUpper();

                    // Save pickup address
                    locmodel.UserAddress = model.Address;
                    var address = AddAddress(model.Address, customer);
                    if (locmodel.Id == 0 || locmodel.Id == oi.CurrentLocationId + 1)
                    {
                        oi = AddLocation(locmodel, customer, address.Id);
                    }
                    else if (locmodel.Id > 0 && locmodel.Id <= oi.CurrentLocationId)
                    {
                        oi = UpdateLocation(locmodel, customer, address.Id, true);
                    }
                    else
                    {
                        ModelState.Clear();
                        ModelState.AddModelError("", "Failed to process order location.");
                        PrepareLocationAddressModelConst(customer, model);
                        return Json(GetJsonView(CommonValues.PickupAddressViewName, model));
                    }

                    // Successfully saved, move to Delivery Address
                    var deliveryAddressId = 0;
                    if (locmodel.Address != null)
                        deliveryAddressId = locmodel.Address.Id;
                    model = new LocationAddressModel();
                    model = PrepareLocationAddressModel(customer, deliveryAddressId, true);
                    ModelState.Clear();

                    if (oi.OrderCompleted)
                    {
                        return UpdateFinalizedOrder(customer, oi);
                    }

                    notices.Add("Pickup address successfully saved.");
                    return Json(GetJsonView(CommonValues.DeliveryAddressViewName, model, notices));
                }

                // ModelState invalid
                PrepareLocationAddressModelConst(customer, model);
                return Json(GetJsonView(CommonValues.PickupAddressViewName, model));
            }
            catch (Exception e)
            {
                ModelState.Clear();
                ModelState.AddModelError("", e.Message);
                PrepareLocationAddressModelConst(customer, model);
                return Json(GetJsonView(CommonValues.PickupAddressViewName, model));
            }
        }

        [HttpPost]
        public ActionResult DeliveryAddress(LocationAddressModel model, bool selectAddress = false)
        {
            var customer = _workContext.CurrentCustomer;
            var notices = new List<string>();
            var warnings = new List<string>();
            if (selectAddress && model.SelectedAddressId.HasValue && model.SelectedAddressId != 0)
            {
                model.Address.Id = model.SelectedAddressId.Value;
            }
            PrepareLocationAddressModelConst(customer, model);
            try
            {
                var oi = LoadOrderInformation(customer);
                if (oi == null)
                    oi = InitOrder(customer);

                var li = oi.Locations.SingleOrDefault(l => l.Id == oi.CurrentLocationId);
                var locmodel = new OrderLocationModel();
                if (li == null)
                {
                    locmodel = PrepareLocationModel(customer, null, oi.UserAddressId);
                }
                else
                {
                    locmodel = PrepareLocationModel(customer, li, oi.UserAddressId);
                }

                if (selectAddress)
                {
                    ModelState.Clear();
                    if (model.SelectedAddressId.HasValue && model.SelectedAddressId != 0)
                    {
                        var addr = customer.Addresses.FirstOrDefault(a => a.Id == model.SelectedAddressId);
                        if (addr != null)
                        {
                            model.Address = PrepareAddressModel(addr);
                            model.Address.IsTypeRequired = true;
                            model.IsAddressTypeVerified = addr.IsAddressTypeVerified();
                            notices.Add("Delivery address selected.");
                        }
                        else
                        {
                            ModelState.AddModelError("", "Selected address does not exist.");
                        }
                        model.SelectedAddressId = null;
                    }
                    return Json(GetJsonView(CommonValues.DeliveryAddressViewName, model, notices));
                }

                // Check inter-district
                if (!String.IsNullOrEmpty(locmodel.UserAddress.ZipPostalCode) && !String.IsNullOrEmpty(model.Address.ZipPostalCode))
                {
                    var district1 = _logisticsService.GetDistrictByZipPostalCode(locmodel.UserAddress.ZipPostalCode);
                    var district2 = _logisticsService.GetDistrictByZipPostalCode(model.Address.ZipPostalCode);
                    if (district1 != null && district2 != null && district1.Id != district2.Id)
                    {
                        ModelState.AddModelError("", "Inter-district shipping between " + district1.Name + " and " + district2.Name + " is currently not allowed.");
                        return Json(GetJsonView(CommonValues.DeliveryAddressViewName, model));
                    }
                }

                // Check address type
                if (!model.Address.Type.HasValue)
                    ModelState.AddModelError("", "Address type must be selected.");

                if (ModelState.IsValid)
                {
                    // Remove spaces in the postal code.
                    model.Address.ZipPostalCode = model.Address.ZipPostalCode.Replace(" ", string.Empty).Trim().ToUpper();

                    // Save delivery address
                    locmodel.Address = model.Address;

                    //business addr type must have signature
                    if (locmodel.Address == null || locmodel.Address.Type.Value == AddressType.Business || !model.NoSignature.HasValue)
                    {
                        locmodel.NoSignature = false;
                    }
                    else
                    {
                        locmodel.NoSignature = model.NoSignature.Value;
                    }

                    var address = AddAddress(model.Address, customer);
                    if (address != null)
                    {
                        locmodel.Address = model.Address;
                    }

                    
                    if (locmodel.Id == 0 || locmodel.Id == oi.CurrentLocationId + 1)
                    {
                        oi = AddLocation(locmodel, customer, oi.UserAddressId.HasValue ? oi.UserAddressId.Value : customer.ShippingAddress.Id);
                    }
                    else if (locmodel.Id > 0 && locmodel.Id <= oi.CurrentLocationId)
                    {
                        oi = UpdateLocation(locmodel, customer, oi.UserAddressId.Value);
                    }
                    else
                    {
                        ModelState.Clear();
                        ModelState.AddModelError("", "Failed to process order location.");
                        return Json(GetJsonView(CommonValues.DeliveryAddressViewName, model));
                    }

                    // Successfully saved, move to next

                    ModelState.Clear();
                    notices.Add("Delivery address successfully saved.");
                    li = LoadCurrentLocation(oi);

                    // Update location info options
                    if (li.Packages != null && locmodel.Address != null && locmodel.Address.Type.HasValue)
                    {
                        li.IsResidential = IsResidential(locmodel.Address.Type.Value, li.NoSignature.Value);
                        if (li.IsResidential)
                        {
                            li.Packages.ForEach(p => p.AddtionalAttributes |= AddtionalAttributes.Residential);
                        }
                        else
                        {
                            if (li.Packages.Count > 0 && li.Packages.First().AddtionalAttributes.HasFlag(AddtionalAttributes.Residential))
                            {
                                li.Packages.ForEach(p => p.AddtionalAttributes &= ~AddtionalAttributes.Residential);
                            }
                        }
                        if (li.NoSignature.Value)
                        {
                            li.Packages.ForEach(p => p.AddtionalAttributes |= AddtionalAttributes.NoSignature);

                            if (li.Packages.Any(p => p.InsuranceValue.HasValue))
                            {
                                li.Packages.ForEach(p => p.AddtionalAttributes &= ~AddtionalAttributes.Insurance);
                                li.Packages.ForEach(p => p.InsuranceValue = null);
                            }
                        }
                        else
                        {
                            li.Packages.ForEach(p => p.AddtionalAttributes &= ~AddtionalAttributes.NoSignature);
                        }
                    }

                    // Reference
                    if (model.OrderByInfo != li.OrderInfo)
                    {
                        li.OrderInfo = model.OrderByInfo;
                    }
                    if (model.AdditionalInstruction != li.AddtionalInstruction)
                    {
                        li.AddtionalInstruction = model.AdditionalInstruction;
                    }

                    _orderProcessingService.SaveOrderInformation(oi, customer);

                    if (oi.OrderCompleted)
                    {
                        return UpdateFinalizedOrder(customer, oi);
                    }

                    return GetPackageView(li);
                }

                // ModelState invalid
                return Json(GetJsonView(CommonValues.DeliveryAddressViewName, model));
            }
            catch (Exception e)
            {
                ModelState.Clear();
                ModelState.AddModelError("", e.Message);
                return Json(GetJsonView(CommonValues.DeliveryAddressViewName, model));
            }
        }

        [HttpPost]
        public ActionResult BillingAddress(LocationAddressModel model, bool selectAddress = false)
        {
            var customer = _workContext.CurrentCustomer;
            var notices = new List<string>();
            var warnings = new List<string>();
            if (selectAddress && model.SelectedAddressId.HasValue && model.SelectedAddressId != 0)
            {
                model.Address.Id = model.SelectedAddressId.Value;
            }
            PrepareLocationAddressModelConst(customer, model);
            if (selectAddress)
            {
                ModelState.Clear();
                if (model.SelectedAddressId.HasValue && model.SelectedAddressId != 0)
                {
                    var addr = customer.Addresses.FirstOrDefault(a => a.Id == model.SelectedAddressId);
                    if (addr != null)
                    {
                        model.Address = PrepareAddressModel(addr);
                        model.Address.IsTypeRequired = true;
                        model.IsAddressTypeVerified = addr.IsAddressTypeVerified();
                        notices.Add("Billing address selected.");
                    }
                    else
                    {
                        ModelState.AddModelError("", "Selected address does not exist.");
                    }
                    PrepareLocationAddressModelConst(customer, model);
                }
                return Json(GetJsonView(CommonValues.BillingAddressViewName, model, notices));
            }
            if (ModelState.IsValid)
            {
                // Remove spaces in the postal code.
                model.Address.ZipPostalCode = model.Address.ZipPostalCode.Replace(" ", string.Empty).Trim().ToUpper();

                // Save billing address
                var address = AddAddress(model.Address, customer);
                if (address != null)
                {
                    customer.BillingAddress = address;
                    _customerService.UpdateCustomer(customer);
                }
                else
                {
                    ModelState.Clear();
                    ModelState.AddModelError("", "Failed to process billing address.");
                    return Json(GetJsonView(CommonValues.BillingAddressViewName, model));
                }

                // Successfully saved, move to next
                ModelState.Clear();
                notices.Add("Billing address successfully saved.");
                var oi = LoadOrderInformation(customer);
                var charge = _orderProcessingService.CaculateTotal(oi, customer);
                var paymentInfo = LoadPaymentInformation(customer);
                // Billing
                if (!oi.OrderCompleted && !_orderProcessingService.IsPaymentExempt(customer) && (customer.BillingAddress == null || charge.SubTotalInclDiscount > 0))
                {
                    return CheckPayment(customer, oi, charge);
                }
                oi.OrderCompleted = true;
                _orderProcessingService.SaveOrderInformation(oi, customer);
                // All done, go to final confirmation
                return Json(GetJsonView(CommonValues.FinalizeViewName, null));
            }

            // ModelState invalid
            return Json(GetJsonView(CommonValues.BillingAddressViewName, model));
        }

        public ActionResult DiscardPendingOrder()
        {
            var customer = _workContext.CurrentCustomer;
            _orderProcessingService.RemoveCurrentOrder(customer, true);

            return RedirectToAction("OrderInfo");
        }

        public ActionResult UserProfile()
        {
            var customer = _workContext.CurrentCustomer;

            if (!customer.IsRegistered())
            {
                return AccessDeniedView();
            }

            var model = PrepareAddressModel(customer.ShippingAddress);

            model.Email = customer.Email;
            model.FirstName = customer.GetAttribute<string>(SystemCustomerAttributeNames.FirstName);
            model.LastName = customer.GetAttribute<string>(SystemCustomerAttributeNames.LastName);
            model.Company = customer.GetAttribute<string>(SystemCustomerAttributeNames.Company);
            model.PhoneNumber = customer.GetAttribute<string>(SystemCustomerAttributeNames.Phone);
            model.CellPhoneNumber = customer.GetAttribute<string>(SystemCustomerAttributeNames.CellPhone);


            var accType = _orderProcessingService.GetChargeRule(customer);
            switch (accType)
            {
                case ChargeRule.Membership:
                    ViewBag.AccountType = _localizationService.GetResource("Logistics.AccountType.Membership");
                    ViewBag.AccountWarning = null;
                    break;
                case ChargeRule.Volume:
                    ViewBag.AccountType = _localizationService.GetResource("Logistics.AccountType.Volume");
                    ViewBag.AccountWarning = _localizationService.GetResource("Logistics.AccountWarning.Volume");
                    break;
                case ChargeRule.PayPerUse:
                default:
                    ViewBag.AccountType = _localizationService.GetResource("Logistics.AccountType.PayPerUse");
                    ViewBag.AccountWarning = null;
                    break;
            }

            ViewBag.PreferredPaymentMethod = "";
            if (accType == ChargeRule.Membership || accType == ChargeRule.Volume)
            {
                switch ((InvoiceStatus)customer.GetAttribute<int>(SystemCustomerAttributeNames.PreferredPaymentMethodId))
                {
                    case InvoiceStatus.PaidByCheck:
                        ViewBag.PreferredPaymentMethod = _localizationService.GetResource("Logistics.PaymentMethod.PayByCheck");
                        break;
                    case InvoiceStatus.PaidByCreditCard:
                        ViewBag.PreferredPaymentMethod = _localizationService.GetResource("Logistics.PaymentMethod.PayByCreditCard");
                        break;
                    default:
                        break;
                }
            }

            ViewBag.HasPendingOrderKey = _orderProcessingService.HasPendingOrders(customer);
            return PartialView(model);
        }

        [NonAction]
        protected PackageModel PreparePackageModelForList(LogisticsPackage package)
        {
            var ct = _dateTimeHelper.ConvertToUserTime(package.CreatedOnUtc, System.DateTimeKind.Utc);
            DateTime? pt = null, dt = null, rt = null;

            //Package note info by admin
            var pkgNoteByAdmin = package.PackageNotes.Where(pkn => pkn.DisplayToCustomer && pkn.TypeId == (int)PackageNoteType.AdminNote)
                                                    .OrderByDescending(pkn => pkn.CreatedOnUtc)
                                                    .Select(pkn => pkn.Note)
                                                    .FirstOrDefault();

            if (package.Shipment.ShippedDateUtc.HasValue)
            {
                pt = _dateTimeHelper.ConvertToUserTime(package.Shipment.ShippedDateUtc.Value, DateTimeKind.Utc);
            }

            if (package.Shipment.DeliveryDateUtc.HasValue)
            {
                dt = _dateTimeHelper.ConvertToUserTime(package.Shipment.DeliveryDateUtc.Value, DateTimeKind.Utc);
            }

            if (package.ReadyOnUtc.HasValue)
            {
                rt = _dateTimeHelper.ConvertToUserTime(package.ReadyOnUtc.Value);
            }

            var baseWeight = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId);
            var baseWeightIn = baseWeight != null ? baseWeight.Name : "";
            var baseDimension = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId);
            var baseDimensionIn = baseDimension != null ? baseDimension.Name : "";

            StringBuilder pkgMeasure = new StringBuilder();
            if (package.Shipment.Length.HasValue && package.Shipment.Width.HasValue && package.Shipment.Height.HasValue)
            {
                pkgMeasure.AppendFormat("{1:F1} x {2:F1} x {3:F1} ({0})", baseDimensionIn, package.Shipment.Length.Value, package.Shipment.Width.Value, package.Shipment.Height.Value);

                if (package.Shipment.TotalWeight.HasValue)
                {
                    pkgMeasure.AppendFormat(" {0:F1} ({1})", package.Shipment.TotalWeight.Value, baseWeightIn);
                }
            }
            else
            {
                pkgMeasure.Append(_localizationService.GetResource("Logistics.StandardPackage"));
            }

            if (package.Shipment.Attributes.HasValue && package.Shipment.Attributes.Value != 0)
            {
                pkgMeasure.Append(" ");
                pkgMeasure.Append(((AddtionalAttributes)package.Shipment.Attributes).GetLocalizedEnum(_localizationService, _workContext.WorkingLanguage.Id));
            }

            if (package.Shipment.InsuranceValue.HasValue && package.Shipment.InsuranceValue.Value != 0)
            {
                if (pkgMeasure.Length > 0)
                    pkgMeasure.Append(" ");
                pkgMeasure.AppendFormat(_localizationService.GetResource("Logistics.Order.InsuranceFormat"), package.Shipment.InsuranceValue.Value);
            }

            var statusStr = package.Status.GetLocalizedEnum(_localizationService, _workContext);
            return new PackageModel()
            {
                Id = package.Id,
                CreatedTime = ct,
                CreatedTimeStr = ct.ToNoSecondString(_workContext),
                PickupTime = pt,
                PickupTimeStr = pt.ToNoSecondString(_workContext, _localizationService),
                DeliveryTime = dt,
                DeliveryTimeStr = dt.ToNoSecondString(_workContext, _localizationService),
                Reference = package.Shipment.Comment,
                DeliveryAddress = package.DeliveryLocation.Address.FormatAddressShort(),
                PickUpAddress = package.PickUpLocation.Address.FormatAddressShort(),
                Status = ((package.Status == ShippingStatus.ReadyForPickUp || package.Status == ShippingStatus.NotYetReady) && rt.HasValue) ?
                    rt.Value.ToNoSecondString(_workContext) :
                    (pkgNoteByAdmin == null ? statusStr : String.Format(_localizationService.GetResource("Logistics.MyHub.StatusWithNote"), statusStr, pkgNoteByAdmin)),
                ShipmentId = package.ShipmentId,
                OrderId = package.Shipment.OrderId,
                TrackingNumber = package.Shipment == null ? "N/A" : package.Shipment.TrackingNumber,
                PackageMeasurement = pkgMeasure.ToString(),
                OrderBy = package.ApplicatorId.HasValue ? _customerService.GetCustomerById(package.ApplicatorId.Value).GetFullName() : ""
            };
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult _OnGoingPackageList(GridCommand command, PackageListModel model)
        {
            var customer = _workContext.CurrentCustomer;

            var orderHistoryInMonths = _logisticsInformationSettings.OrderHistoryInMonths == 0 ? 2 : _logisticsInformationSettings.OrderHistoryInMonths;
            DateTime? startDate = DateTime.UtcNow.AddMonths(orderHistoryInMonths * -1);
            DateTime? endDate = DateTime.UtcNow;

            var pkgs = _logisticsService.SearchTrackingPackages(
                customer.Id,
                startDate,
                endDate,
                null,
                command.Page - 1,
                command.PageSize);

            var gridModel = new GridModel<PackageModel>
            {
                Data = pkgs.Select(PreparePackageModelForList),
                Total = pkgs.TotalCount
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        #endregion

        #region Invoice

        [ChildActionOnly]
        public ActionResult InvoiceList()
        {
            var invoices = _invoiceService.GetInvoicesByCustomerId(_workContext.CurrentCustomer.Id);

            if (invoices == null || invoices.Count == 0)
            {
                return View(new List<InvoiceModel>());
            }

            if (invoices.Any(x => x.InvoiceStatus == InvoiceStatus.Published))
            {
                var totalDue = _invoiceService.GetTotalDueByCustomer(_workContext.CurrentCustomer);
                if (totalDue > 0)
                {
                    // invoice must not be empty here, so get the latest invoice due date.
                    var dueDate = invoices.First().DueDate;
                    if (dueDate.HasValue)
                    {
                        ViewBag.DueWarning = String.Format(
                            _localizationService.GetResource("Logistics.Order.Invoice.TotalDue"),
                            _priceFormatter.FormatPrice(totalDue),
                            dueDate.Value.ToString("MM/dd/yyyy"));
                    }
                }
            }

            var model = invoices.Select(i => new InvoiceModel()
            {
                Id = i.Id,
                Date = _dateTimeHelper.ConvertToUserTime(i.CreatedOnUtc).ToLongDateString(),
                Amount = _priceFormatter.FormatPrice(i.Total),
                InvoiceNumber = i.InvoiceNumber,
                InvoiceStatus = i.InvoiceStatus.GetLocalizedEnum(_localizationService, _workContext)
            }).ToList();

            return View(model);
        }

        public ActionResult Invoice(int id)
        {
            var invoice = _invoiceService.GetInvoiceById(id);

            if (invoice == null || _workContext.CurrentCustomer.Id != invoice.CustomerId)
            {
                return RedirectToAction("Index");
            }

            var res = PdfInvoice(invoice);
            if (res == null || !(res is FileContentResult))
            {
                LogCustomerActivity("Try to download invalid invoice {0}", _workContext.CurrentCustomer, _customerActivityService, invoice.Id);
                return RedirectToAction("Index");
            }

            return res;
        }

        public ActionResult DownloadInvoice(string number, Guid guid)
        {
            var invoice = _invoiceService.GetInvoiceByNumber(number);
            if (invoice.InvoiceGuid != guid)
            {
                LogError(String.Format("Failed to download invoice (#{0}) due to GUID ({1})mismatch.", number, guid));
                return RedirectToRoute("HomePage");
            }

            return PdfInvoice(invoice);
        }

        [NonAction]
        private ActionResult PdfInvoice(Invoice invoice)
        {
            bool canDownload = (invoice.InvoiceStatus != InvoiceStatus.Published) ||
                (invoice.InvoiceStatus != InvoiceStatus.Paid) ||
                (invoice.InvoiceStatus != InvoiceStatus.PaidByCreditCard) ||
                (invoice.InvoiceStatus != InvoiceStatus.PaidByCheck);
            if (!canDownload)
            {
                return null;
            }

            byte[] bytes = null;
            using (var stream = new System.IO.MemoryStream())
            {
                _pdfService.PrintInvoiceToPdf(stream, invoice, _workContext.WorkingLanguage, _invoiceSettings.UseCustomerTimeZone);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", String.Format("invoice_{0}.pdf", invoice.InvoiceNumber));
        }

        #endregion

        #region Summary

        [ChildActionOnly]
        public ActionResult Summary()
        {
            try
            {
                var model = PrepareSummaryModel(_workContext.CurrentCustomer);
                return PartialView(model);
            }
            catch (NopException)
            {
                _orderProcessingService.RemoveCurrentOrder(_workContext.CurrentCustomer);
                return Content("");
            }
        }

        #endregion

        #region Order Information

        public ActionResult OrderInfo()
        {
            return RedirectToAction("Index", "Order");
        }

        [HttpPost]
        public ActionResult InitView(string addMoreLocation = "", bool bypassPending = false)
        {
            var customer = _workContext.CurrentCustomer;
            if (!customer.IsRegistered())
            {
                return Json(GetJsonView(CommonValues.AccessDeniedViewName, null));
            }

            var profiler = MiniProfiler.Current;
            var oi = LoadOrderInformation(customer);
            var li = LoadCurrentLocation(oi);
            try
            {
                if (_orderProcessingService.HasPendingOrders(customer) && !bypassPending && Request.Cookies["logistics-" + customer.Id] == null)
                {
                    return Json(GetJsonView(CommonValues.PendingOrderViewName, null));
                }
                if (oi == null || li == null)
                {
                    // No order info, go to pickup address
                    if (oi == null)
                        oi = InitOrder(customer);
                    var model = PrepareLocationAddressModel(customer, -1, true);

                    return Json(GetJsonView(CommonValues.PickupAddressViewName, model));

                    //oi = InitOrder(customer);
                    //var model = PrepareLocationModel(customer, null, oi.UserAddressId);
                    //return Json(GetJsonView(CommonValues.LocationInfoViewName, model, true));
                }
                else
                {
                    // If no pickup address saved, go to pickup address
                    var userAddress = oi.UserAddressId.HasValue ? _addressService.GetAddressById(oi.UserAddressId.Value) : (customer.ShippingAddress != null ? customer.ShippingAddress : null);
                    if (userAddress == null)
                    {
                        var model = PrepareLocationAddressModel(customer, -1, true);
                        return Json(GetJsonView(CommonValues.PickupAddressViewName, model));
                    }

                    // If no delivery address saved, go to delivery address
                    var address = li != null && li.AddressId > 0 ? _addressService.GetAddressById(li.AddressId) : null;
                    if (address == null)
                    {
                        var model = PrepareLocationAddressModel(customer, 0, true);
                        return Json(GetJsonView(CommonValues.DeliveryAddressViewName, model));
                    }

                    // Need to save shipping options
                    //if (String.IsNullOrEmpty(li.OrderInfo))
                    //{
                    //    var model = PrepareShippingOptionModel(customer, li, null);
                    //    return Json(GetJsonView(CommonValues.ShippingOptionViewName, model));
                    //}


                    // Need to save packages, go to packages
                    if (li.Packages.Count < 1 || li.CurrentPackageId > li.Packages.Count)
                    {
                        return GetPackageView(li);
                    }

                    var charge = _orderProcessingService.CaculateTotal(oi, customer);
                    var paymentInfo = LoadPaymentInformation(customer);
                    bool paymentMatch = false;
                    if (paymentInfo != null && charge.Total > 0 && paymentInfo.CreditCardId > 0 && paymentInfo.OrderTotal == charge.Total)
                    {
                        paymentMatch = true;
                    }
                    // Billing
                    if (!_orderProcessingService.IsPaymentExempt(customer) && (customer.BillingAddress == null || !paymentMatch))
                    {
                        return CheckPayment(customer, oi, charge);
                    }
                    oi.OrderCompleted = true;
                    _orderProcessingService.SaveOrderInformation(oi, customer);
                    // All done, go to final confirmation
                    return Json(GetJsonView(CommonValues.FinalizeViewName, null));
                }
            }
            catch (Exception e)
            {
                return FatalErrorJsonView(e.Message, customer, e, true);
            }
        }

        [HttpPost]
        public ActionResult PageRequest(string request = "", int? packageId = null)
        {
            var customer = _workContext.CurrentCustomer;
            var oi = LoadOrderInformation(customer);
            var li = LoadCurrentLocation(oi);
            ModelState.Clear();
            switch (request)
            {
                case CommonValues.PickupAddressViewName:
                    if (oi.UserAddressId > 0)
                    {
                        var pickaddrmodel = PrepareLocationAddressModel(customer, oi.UserAddressId, true);
                        return Json(GetJsonView(CommonValues.PickupAddressViewName, pickaddrmodel));
                    }
                    else
                    {
                        var pickaddrmodel = PrepareLocationAddressModel(customer, -1, true);
                        return Json(GetJsonView(CommonValues.PickupAddressViewName, pickaddrmodel));
                    }

                case CommonValues.DeliveryAddressViewName:
                    if (li.AddressId > 0)
                    {
                        var deladdrmodel = PrepareLocationAddressModel(customer, li.AddressId, true);
                        return Json(GetJsonView(CommonValues.DeliveryAddressViewName, deladdrmodel));
                    }
                    else
                    {
                        var deladdrmodel = PrepareLocationAddressModel(customer, 0, true);
                        return Json(GetJsonView(CommonValues.DeliveryAddressViewName, deladdrmodel));
                    }

                case CommonValues.ShippingOptionViewName:
                case CommonValues.PackageInfoViewName:
                    return GetPackageView(li, packageId);

                case CommonValues.BillingAddressViewName:
                    var addrid = customer.BillingAddress != null && customer.BillingAddress.Id > 0 ? customer.BillingAddress.Id : 0;
                    var billaddrmodel = PrepareLocationAddressModel(customer, addrid);
                    return Json(GetJsonView(CommonValues.BillingAddressViewName, billaddrmodel));

                case CommonValues.PaymentInfoViewName:
                    var charge = _orderProcessingService.CaculateTotal(oi, customer);
                    if (!_orderProcessingService.IsPaymentExempt(customer) && (customer.BillingAddress == null || charge.SubTotalInclDiscount > 0))
                    {
                        return CheckPayment(customer, oi, charge);
                    }
                    break;

                case CommonValues.FinalizeViewName:
                    return UpdateFinalizedOrder(customer, oi);
            }
            return InitView();
        }

        [HttpPost]
        [ParameterBasedOnFormNameAttribute("selectaddress", "selectingAddress")]
        public ActionResult Location(OrderLocationModel model, bool selectingAddress)
        {
            var customer = _workContext.CurrentCustomer;
            var maxPkg = _permissionService.Authorize(StandardPermissionProvider.LogisticsVolumeShipping) ?
                _logisticsInformationSettings.VolumeMaxPackagePerLocation :
                _logisticsInformationSettings.MaxPackagePerLocation;
            return Json(GetJsonView(CommonValues.LocationInfoViewName, model));
            //try
            //{
            //    var region = _logisticsService.GetRegionById(model.SelectedRegionId);
            //    if (!selectingAddress)
            //    {
            //        if (region == null)
            //        {
            //            ModelState.AddModelError("", "Selected region is invalid.");
            //        }
            //        else
            //        {
            //            var regionName = region.GetLocalized(r => r.Name);
            //            if (!_logisticsService.IsZipPostalCodeInRegion(region, model.UserAddress.ZipPostalCode))
            //            {
            //                ModelState.AddModelError("", String.Format(_localizationService.GetResource("Address.Fields.Zippostalcode.PickupLocation.OutofRegion"), regionName));
            //            }
            //            if (!_logisticsService.IsZipPostalCodeInRegion(region, model.Address.ZipPostalCode))
            //            {
            //                ModelState.AddModelError("", String.Format(_localizationService.GetResource("Address.Fields.Zippostalcode.DeliveryLocation.OutofRegion"), regionName));
            //            }
            //        }
            //    }

            //    var oi = LoadOrderInformation(customer);

            //    var tli = oi.Locations.FirstOrDefault(l => l.Id == oi.CurrentLocationId && model.Id != 0);
            //    if (tli != null && model.PackageNumber > 0 && model.PackageNumber < tli.Packages.Count)
            //    {
            //        ModelState.AddModelError("", "The package number cannot be changed. You may go to the homepage to discard the current order.");
            //    }

            //    if (oi.CurrentLocationId > _logisticsInformationSettings.MaxLocationNumber)
            //    {
            //        ModelState.AddModelError("", "Exceeds the orders limitation.");
            //    }

            //    if (!selectingAddress && ModelState.IsValid)
            //    {
            //        if (!model.NoSignature.HasValue || model.Address.Type == AddressType.Business)
            //            model.NoSignature = false;

            //        // Update customer region
            //        if (customer.Region == null)
            //        {
            //            customer.Region = region;
            //            _customerService.UpdateCustomer(customer);
            //        }

            //        // Remove spaces in the postal code.
            //        model.Address.ZipPostalCode = model.Address.ZipPostalCode.Replace(" ", string.Empty).Trim().ToUpper();
            //        model.UserAddress.ZipPostalCode = model.UserAddress.ZipPostalCode.Replace(" ", string.Empty).Trim().ToUpper();

            //        // Update user shipping address
            //        var address = AddAddress(model.UserAddress, customer);

            //        if (model.Id == 0 || model.Id == oi.CurrentLocationId + 1)
            //        {
            //            oi = AddLocation(model, customer, address.Id);
            //        }
            //        else if (model.Id > 0 && model.Id <= oi.CurrentLocationId)
            //        {
            //            oi = UpdateLocation(model, customer, address.Id);
            //        }
            //        else
            //        {
            //            return FatalErrorJsonView(String.Format("The location ID is invalid. {0}", model.Id), customer);
            //        }

            //        //bool isExpress = !String.IsNullOrWhiteSpace(express);
            //        // Disable express checkout
            //        bool isExpress = false;

            //        var li = oi.Locations.Single(l => l.Id == oi.CurrentLocationId);

            //        if (oi.OrderCompleted)
            //        {
            //            var model2 = PrepareDetailViewModel(customer, li, oi.UserAddressId);
            //            return Json(GetJsonView(CommonValues.DetailViewName, model2));
            //        }

            //        OrderPackageModel pkgModel = null;
            //        if (li.Packages.Count == 0)
            //        {
            //            pkgModel = PreparePackageModel(null, null);
            //            pkgModel.InsuranceEnabled = InsuranceEnabled(li);
            //            pkgModel.PackageId = 1;
            //            pkgModel.ShowNextPackage = li.PackageNumber > 1;
            //            pkgModel.PackageCount = 0;

            //            if (isExpress)
            //            {
            //                li = AddPackage(pkgModel, customer);
            //                var model2 = PrepareDetailViewModel(customer, li, oi.UserAddressId);
            //                return Json(GetJsonView(CommonValues.DetailViewName, model2));
            //            }
            //        }
            //        else
            //        {
            //            var pi = li.Packages.OrderBy(p => p.Id).Last();
            //            pkgModel = PreparePackageModel(pi, li.Packages.Where(p => p.Id != pi.Id));
            //            pkgModel.InsuranceEnabled = InsuranceEnabled(li);
            //            pkgModel.Cost = CalculatePackageCost(li, customer, pi.Id);
            //            pkgModel.ShowNextPackage = li.Packages.Count < li.PackageNumber;
            //            pkgModel.PackageCount = li.Packages.Count;
            //        }

            //        return Json(GetJsonView(CommonValues.PackageInfoViewName, pkgModel, pkgModel.PackageId));
            //    }

            //    if (selectingAddress)
            //    {
            //        ModelState.Clear();
            //        if (model.SelectedAddressId.HasValue && model.SelectedAddressId != 0)
            //        {
            //            var addr = customer.Addresses.FirstOrDefault(a => a.Id == model.SelectedAddressId);
            //            if (addr != null)
            //            {
            //                model.Address = PrepareAddressModel(addr, region);
            //                model.Address.IsTypeRequired = true;
            //                model.IsAddressTypeVerified = addr.IsAddressTypeVerified();
            //            }
            //            model.SelectedAddressId = null;
            //        }
            //        if (model.SelectedUserAddressId.HasValue && model.SelectedUserAddressId != 0)
            //        {
            //            var addr = customer.Addresses.FirstOrDefault(a => a.Id == model.SelectedUserAddressId);
            //            if (addr != null)
            //            {
            //                model.UserAddress = PrepareAddressModel(addr, region);
            //                model.UserAddress.IsTypeRequired = true;
            //                model.IsUserAddressTypeVerified = addr.IsAddressTypeVerified();
            //            }
            //            model.SelectedUserAddressId = null;
            //        }
            //    }

            //    PrepareLocationModelConst(customer, model);
            //    PreparePickupTime(tli, model);
            //    return Json(GetJsonView(CommonValues.LocationInfoViewName, model));
            //}
            //catch (Exception e)
            //{
            //    return FatalErrorJsonView(e.Message, customer, e);
            //}
        }

        [HttpPost]
        public ActionResult GetProvinceNameByRegion(int regionId)
        {
            string provinceName = "";
            int provinceId = 0;

            var region = _logisticsService.GetRegionById(regionId);
            if (region != null)
            {
                var province = AddressHelper.GetProvinceByRegion(region);
                provinceName = province.GetLocalized(x => x.Name);
                provinceId = province.Id;
            }
            return Json(new { name = provinceName, id = provinceId });
        }

        [HttpPost]
        [ParameterBasedOnFormNameAttribute("caculate", "caculatePrice")]
        public ActionResult Package(OrderPackageModel model, string packageAction, int? requestpackageid = 0)
        {

            var customer = _workContext.CurrentCustomer;
            var oi = LoadOrderInformation(customer);
            var li = LoadCurrentLocation(oi);

            //var appliedCards = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer).Where(gc => gc.GiftCardType == GiftCardType.ExpressPackage);
            //if ((appliedCards == null || appliedCards.Count() == 0) && customer.CustomerRoleMappings.Any(c => c.CustomerRoleId != 9))
            //{
            //	throw new Exception("Dear Pay Per Use Customer,<br/><br/><br/><br/>" +
            //				   "Our payment system is currently under maintenance and we sincerely apologize for the inconvenience." +
            //				   "<br/> Please contact SpotsHUB Shipping Department at 604-944-2286 to acquire a free shipping code for your shipment during our maintenance period. " +
            //				   "<br/><br/><br/><br/>Thank you for your understanding.SpotsHUB Shipping Department");
            //	//return Content("Dear Pay Per Use Customer,<br/><br/><br/><br/>" +
            //	//               "Our payment system is currently under maintenance and we sincerely apologize for the inconvenience." +
            //	//               "<br/> Please contact SpotsHUB Shipping Department at 604-944-2286 to acquire a free shipping code for your shipment during our maintenance period. " +
            //	//               "<br/><br/><br/><br/>Thank you for your understanding.SpotsHUB Shipping Department"); 

            //}

            try
            {
                var packageId = model.PackageId;

                #region Load Package

                // Load an existing package
                if (packageAction == "loadPackage")
                {
                    packageId = requestpackageid.Value;
                    ModelState.Clear();
                    model = new OrderPackageModel();

                    if (li == null || li.Packages == null || li.Packages.Count == 0)
                    {
                        ModelState.AddModelError("", "Package " + packageId + " cannot be loaded because there are no saved packages in your order.");
                        packageId = model.PackageId;
                    }
                    var pi = li.Packages.SingleOrDefault(p => p.Id == packageId);
                    if (pi == null)
                    {
                        ModelState.AddModelError("", "Package " + packageId + " cannot be loaded because it is not in your order.");
                        packageId = model.PackageId;
                    }
                    if (li.Packages.Count > 0)
                    {
                        model = PreparePackageModel(pi, li.Packages.Where(p => p.Id != packageId));
                        model.Cost = CalculatePackageCost(li, customer, pi.Id);
                        model.PackageId = packageId;
                    }
                    else
                    {
                        model = PreparePackageModel(null, li.Packages);
                        model.PackageId = li.Packages.Count + 1;
                    }
                    model.InsuranceEnabled = InsuranceEnabled(li);
                    model.PackageCount = li.Packages.Count;

                    return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
                }

                #endregion

                #region Remove Package

                // Remove an existing package
                if (packageAction == "remove")
                {
                    ModelState.Clear();
                    if (li == null || li.Packages == null || li.Packages.Count == 0)
                    {
                        // No saved package
                        ModelState.AddModelError("", "Package " + packageId + " cannot be removed because there is no package in your order.");
                        return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
                    }
                    if (!li.Packages.Any(p => p.Id == packageId) && packageId != li.Packages.Count + 1)
                    {
                        // Package id not found and not newly added package
                        ModelState.AddModelError("", "Package " + packageId + " cannot be removed because is it not found in your order.");
                        return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
                    }
                    if (packageId != li.Packages.Count + 1)
                    {
                        // Package exists
                        // Remove package from location info
                        li.Packages.RemoveAt(packageId - 1);

                        // Reset existing package Ids
                        if (li.Packages.Count > 0)
                        {
                            foreach (var pkg in li.Packages)
                            {
                                if (pkg.Id > packageId)
                                {
                                    pkg.Id -= 1;
                                }
                            }
                        }
                        _orderProcessingService.SaveOrderInformation(oi, customer);
                    }

                    // Render next package view
                    if (li.Packages.Count > 0)
                    {
                        var newId = packageId - 1;
                        if (newId < 1)
                            newId = li.Packages.Count;
                        var pi = li.Packages.Single(p => p.Id == newId);
                        model = PreparePackageModel(pi, li.Packages.Where(p => p.Id != pi.Id));
                        model.Cost = CalculatePackageCost(li, customer, pi.Id);
                        model.PackageId = newId;
                    }
                    else
                    {
                        model = PreparePackageModel(null, li.Packages);
                        model.PackageId = li.Packages.Count + 1;
                    }
                    model.InsuranceEnabled = InsuranceEnabled(li);
                    model.PackageCount = li.Packages.Count;

                    return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
                }

                #endregion

                // Save a package if action is add, modify, or continue
                if (model.PackagePreset == null && model.Length.HasValue && model.Width.HasValue && model.Height.HasValue)//!model.IsLettersize
                {
                    var dim = _measureService.GetMeasureDimensionById(model.DimensionMeasureId);
                    if (dim != null)
                    {
                        var max = MathEx.Round(_measureService.ConvertFromPrimaryMeasureDimension(
                            _logisticsInformationSettings.MaxDimensionSize, dim), 0);
                        decimal total = 0;
                        total += model.Length.Value + model.Width.Value + model.Height.Value;
                        if (total > max)
                        {
                            ModelState.AddModelError("", String.Format(_localizationService.GetResource("Logistics.Order.Package.Maximumsize"), (int)max, dim.Name));
                        }
                    }
                }

                if (model.PackageId <= 1 && model.InsuranceValue.HasValue)
                {
                    var giftCards = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer);
                    if (giftCards != null && giftCards.Count > 0)
                    {
                        ModelState.AddModelError("InsuranceValue", _localizationService.GetResource("Logistics.Order.GiftCard.NoInsuranceAllowed"));
                        model.InsuranceValue = null;
                    }
                }

                if (model.PackagePreset.HasValue && model.PackagePreset < 0)
                {
                    ModelState.AddModelError("", "Please select package size.");
                }

                if (ModelState.IsValid)
                {
                    if (model.PackagePreset != null)
                    {
                        model.Length = null;
                        model.Width = null;
                        model.Height = null;
                        model.Weight = null;
                        //model.DimensionMeasureId and model.WeightMeasureId need to be reset
                        model.DimensionMeasureId = _measureSettings.BaseDimensionId;
                        model.WeightMeasureId = _measureSettings.BaseWeightId;

                    }

                    // Save package information
                    if (model.PackageId <= li.Packages.Count && li.Packages.Count > 0)
                    {
                        li = UpdatePackage(model, customer);
                    }
                    else if (model.PackageId == li.Packages.Count + 1)
                    {
                        li = AddPackage(model, customer);
                    }
                    else
                    {
                        ModelState.AddModelError("", "Cannot save package because package id is incorrect.");
                        return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
                    }
                    ModelState.Clear();
                }
                else
                {
                    if (li.Packages.Count > 0)
                    {
                        model = PreparePackageModel(null, li.Packages);
                        if (model.PackageId == 0)
                        {
                            model.PackageId = li.Packages.Count + 1;
                        }
                        model.InsuranceEnabled = InsuranceEnabled(li);
                    }
                    else
                    {
                        model = PreparePackageModel(null, null);
                        if (model.PackageId == 0)
                        {
                            model.PackageId = 1;
                        }
                        model.InsuranceEnabled = InsuranceEnabled(li);
                    }
                    return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
                }

                if (packageAction == "modify")
                {
                    // Render modified package view
                    if (li.Packages.Count > 0)
                    {
                        var pi = li.Packages.Single(p => p.Id == packageId);
                        model = PreparePackageModel(pi, li.Packages.Where(p => p.Id != pi.Id));
                        model.Cost = CalculatePackageCost(li, customer, pi.Id);
                    }
                    else
                    {
                        model = PreparePackageModel(null, li.Packages);
                    }
                    model.InsuranceEnabled = InsuranceEnabled(li);
                    model.PackageCount = li.Packages.Count;
                    model.PackageId = packageId;
                    return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
                }


                // Add a package for editting
                if (packageAction == "add")
                {
                    ModelState.Clear();
                    if (li == null || li.Packages == null || li.Packages.Count == 0 || packageId == li.Packages.Count + 1)
                    {
                        ModelState.AddModelError("", "Unable to add another package because Package " + packageId + " is already a new unsaved package.");
                        return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
                    }
                    var maxPkg = _permissionService.Authorize(StandardPermissionProvider.LogisticsVolumeShipping) ?
                        _logisticsInformationSettings.VolumeMaxPackagePerLocation :
                        _logisticsInformationSettings.MaxPackagePerLocation;

                    if (li.Packages.Count >= maxPkg)
                    {
                        ModelState.AddModelError("", "Unable to add another package because your maximum number of packages allowed per order is" + maxPkg + ".");
                        return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
                    }

                    // Add a new package for editting
                    model = PreparePackageModel(null, li.Packages);
                    model.InsuranceEnabled = InsuranceEnabled(li);
                    model.PackageCount = li.Packages.Count;
                    model.PackageId = li.Packages.Count + 1;
                    return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
                }

                // Finished with the package section, continue to the next step
                if (packageAction == "continue")
                {
                    ModelState.Clear();
                    // Check if at least one package is in the order
                    if (li == null || li.Packages == null || li.Packages.Count == 0)
                    {
                        ModelState.AddModelError("", "You have no package saved for this order.");
                        return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
                    }

                    oi = LoadOrderInformation(customer);

                    if (oi.OrderCompleted)
                    {
                        return UpdateFinalizedOrder(customer, oi);
                    }

                    // Check if payment required
                    var charge = _orderProcessingService.CaculateTotal(oi, customer);

                    if (!oi.OrderCompleted && !_orderProcessingService.IsPaymentExempt(customer) && (customer.BillingAddress == null || charge.SubTotalInclDiscount > 0))
                    {
                        return CheckPayment(customer, oi, charge);
                    }

                    // No payment needed, finalize order
                    return Json(GetJsonView(CommonValues.FinalizeViewName, null));
                }

                // No action performed, return the package view
                ModelState.Clear();
                ModelState.AddModelError("", "No action is specified for the current package.");
                var paki = li.Packages.Single(p => p.Id == packageId);
                model = PreparePackageModel(paki, li.Packages.Where(p => p.Id != paki.Id));
                return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
            }
            catch (Exception e)
            {
                ModelState.Clear();
                ModelState.AddModelError("", e.Message);
                return Json(GetJsonView(CommonValues.PackageInfoViewName, model, null, null, model.PackageId));
            }
        }

        [HttpPost]
        public ActionResult ShippingOption(ShippingOptionModel model)
        {
            var customer = _workContext.CurrentCustomer;
            var oi = LoadOrderInformation(customer);
            var li = oi.Locations
                .SingleOrDefault(l => l.Id == oi.CurrentLocationId);

            try
            {
                ModelState.Clear();
                var location = PrepareLocationModel(customer, li, oi.UserAddressId);
                //business addr type must have signature
                if (location.Address.Type.Value == AddressType.Business || !model.NoSignature.HasValue)
                {
                    li.NoSignature = false;
                }
                else
                {
                    li.NoSignature = model.NoSignature.Value;
                }

                li.IsResidential = IsResidential(location.Address.Type.Value, li.NoSignature.Value);
                if (li.IsResidential)
                {
                    li.Packages.ForEach(p => p.AddtionalAttributes |= AddtionalAttributes.Residential);
                }
                else
                {
                    if (li.Packages.Count > 0 && li.Packages.First().AddtionalAttributes.HasFlag(AddtionalAttributes.Residential))
                    {
                        li.Packages.ForEach(p => p.AddtionalAttributes &= ~AddtionalAttributes.Residential);
                    }
                }


                if (li.NoSignature.Value)
                {
                    li.Packages.ForEach(p => p.AddtionalAttributes |= AddtionalAttributes.NoSignature);

                    if (li.Packages.Any(p => p.InsuranceValue.HasValue))
                    {
                        li.Packages.ForEach(p => p.AddtionalAttributes &= ~AddtionalAttributes.Insurance);
                        li.Packages.ForEach(p => p.InsuranceValue = null);
                    }
                }
                else
                {
                    li.Packages.ForEach(p => p.AddtionalAttributes &= ~AddtionalAttributes.NoSignature);
                }


                if (model.OrderByInfo != li.OrderInfo)
                {
                    li.OrderInfo = model.OrderByInfo;
                }

                var appliedCards = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer).Where(gc => gc.GiftCardType == GiftCardType.ExpressPackage);
                var giftCardApplied = appliedCards.Count() > 0;
                if (!String.IsNullOrEmpty(model.GiftCardCode))
                {
                    var isGiftCardValid = true;
                    if (appliedCards != null)
                    {
                        foreach (var appliedGiftCard in appliedCards)
                        {
                            if (appliedGiftCard.GiftCardCouponCode == model.GiftCardCode)
                            {
                                // Gift card already applied
                                ModelState.AddModelError("", "Gift card is already applied.");
                                isGiftCardValid = false;
                            }
                        }
                    }

                    var giftCard = _giftCardService.GetAllGiftCards(null, null,
                        null, null, model.GiftCardCode, 0, int.MaxValue).FirstOrDefault();

                    if (giftCard == null || giftCard.GiftCardType != GiftCardType.ExpressPackage || !giftCard.IsGiftCardValid())
                    {
                        // Gift card is invalid
                        ModelState.AddModelError("", "Gift card is not valid.");
                        isGiftCardValid = false;
                    }

                    if (isGiftCardValid)
                    {
                        // Gift card valid, allow only one card at anytime
                        foreach (var card in appliedCards)
                        {
                            customer.RemoveGiftCardCouponCode(card.GiftCardCouponCode);
                        }
                        customer.ApplyGiftCardCouponCode(model.GiftCardCode);
                        _customerService.UpdateCustomer(customer);
                        giftCardApplied = true;
                    }

                }

                if (String.IsNullOrEmpty(model.OrderByInfo))
                {
                    ModelState.AddModelError("", "Order name field cannot be empty.");
                }

                if (model.AdditionalInstruction != li.AddtionalInstruction)
                {
                    li.AddtionalInstruction = model.AdditionalInstruction;
                }
                if (model.HasPickupDate)
                {
                    if (!model.PickupDate.HasValue || !model.PickupTimeWindowId.HasValue)
                        ModelState.AddModelError("", "Must select a pickup time.");

                    li.PickupTimeWindowId = model.PickupTimeWindowId;
                    li.PickupDate = model.PickupDate;
                }
                else
                {
                    li.PickupTimeWindowId = null;
                    li.PickupDate = null;
                }
                if (ModelState.IsValid)
                {
                    if (giftCardApplied)
                    {
                        // Remove the insurance value of first package
                        var pkg = li.Packages.FirstOrDefault();
                        if (pkg != null)
                        {
                            pkg.InsuranceValue = null;
                            pkg.AddtionalAttributes &= ~AddtionalAttributes.Insurance;
                        }
                    }

                    _orderProcessingService.SaveOrderInformation(oi, customer);
                    OrderPackageModel pkgModel = null;
                    if (li.Packages.Count == 0)
                    {
                        pkgModel = PreparePackageModel(null, null);
                        pkgModel.InsuranceEnabled = InsuranceEnabled(li) && !giftCardApplied;
                        pkgModel.PackageId = 1;
                        pkgModel.PackageCount = 0;
                    }
                    else
                    {
                        var pi = li.Packages.OrderBy(p => p.Id).Last();
                        pkgModel = PreparePackageModel(pi, li.Packages.Where(p => p.Id != pi.Id));
                        pkgModel.InsuranceEnabled = InsuranceEnabled(li);
                        pkgModel.Cost = CalculatePackageCost(li, customer, pi.Id);
                        pkgModel.PackageCount = li.Packages.Count;
                    }
                    return Json(GetJsonView(CommonValues.PackageInfoViewName, pkgModel, null, null, pkgModel.PackageId));

                }

                // Something is wrong, return the view.
                PrepareShippingOptionModel(customer, li, model);
                return Json(GetJsonView(CommonValues.ShippingOptionViewName, model));
            }
            catch (Exception e)
            {
                ModelState.AddModelError("", e.Message);
                PrepareShippingOptionModel(customer, li, model);
                return Json(GetJsonView(CommonValues.ShippingOptionViewName, model));
            }
        }

        [HttpPost]
        public ActionResult AddGiftCard(string giftCardCode)
        {
            ModelState.Clear();
            var notice = new List<string>();
            if (String.IsNullOrEmpty(giftCardCode))
            {
                return Json(new
                {
                    success = false,
                    message = "Gift card field is empty."
                });
            }
            else
            {
                var customer = _workContext.CurrentCustomer;
                var giftCard = _giftCardService.GetAllGiftCards(null, null,
                        null, null, giftCardCode, 0, int.MaxValue).FirstOrDefault();

                if (giftCard == null || giftCard.GiftCardType != GiftCardType.ExpressPackage || !giftCard.IsGiftCardValid())
                    return Json(new
                    {
                        success = false,
                        message = _localizationService.GetResource("ShoppingCart.GiftCardCouponCode.WrongGiftCard")
                    });
                else
                {
                    var appliedCards = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer).Where(gc => gc.GiftCardType == GiftCardType.ExpressPackage);
                    if (appliedCards != null && appliedCards.Where(ac => ac.GiftCardCouponCode == giftCardCode).Count() > 0)
                        return Json(new
                        {
                            success = false,
                            message = "Gift card is already applied."
                        });
                    else
                    {
                        foreach (var appliedCard in appliedCards)
                        {
                            customer.RemoveGiftCardCouponCode(appliedCard.GiftCardCouponCode);
                        }
                        customer.ApplyGiftCardCouponCode(giftCardCode);
                        _customerService.UpdateCustomer(customer);
                        notice.Add(_localizationService.GetResource("ShoppingCart.GiftCardCouponCode.Applied"));
                    }
                }
                var model = PrepareSummaryModel(customer);
                var html = this.RenderPartialViewToString(CommonValues.SummaryViewName, model);
                return Json(new
                {
                    success = true,
                    html = html
                });
            }
        }

        [HttpPost]
        public ActionResult RemoveGiftCard()
        {
            ModelState.Clear();
            var customer = _workContext.CurrentCustomer;
            var oi = LoadOrderInformation(customer);
            var li = LoadCurrentLocation(oi);
            var giftCards = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer).Where(gc => gc.GiftCardType == GiftCardType.ExpressPackage);
            if (giftCards != null)
            {
                foreach (var giftCard in giftCards)
                {
                    customer.RemoveGiftCardCouponCode(giftCard.GiftCardCouponCode);
                }
                _customerService.UpdateCustomer(customer);
            }

            var model = PrepareSummaryModel(customer);
            var html = this.RenderPartialViewToString(CommonValues.SummaryViewName, model);
            return Json(new
            {
                html = html,
                success = true
            });
        }

        [HttpPost]
        [NopHttpsRequirement(SslRequirement.Yes)]
        public ActionResult CheckPayment(Customer customer = null, OrderInformation oi = null, Charge charge = null)
        {

            if (customer == null)
                customer = _workContext.CurrentCustomer;

            //var appliedCards = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer).Where(gc => gc.GiftCardType == GiftCardType.ExpressPackage);
            //if ((appliedCards == null || appliedCards.Count() == 0) && customer.CustomerRoleMappings.Any(c => c.CustomerRoleId != 9))
            //{
            //    throw new Exception("Dear Pay Per Use Customer,<br/><br/><br/><br/>" +
            //                   "Our payment system is currently under maintenance and we sincerely apologize for the inconvenience." +
            //                   "<br/> Please contact SpotsHUB Shipping Department at 604-944-2286 to acquire a free shipping code for your shipment during our maintenance period. " +
            //                   "<br/><br/><br/><br/>Thank you for your understanding.SpotsHUB Shipping Department");
            //}

            // Check if billing address is set
            if (customer.BillingAddress == null)
            {
                var addressModel = PrepareLocationAddressModel(customer, 0);
                return Json(GetJsonView(CommonValues.BillingAddressViewName, addressModel));
            }

            if (oi == null)
                oi = LoadOrderInformation(customer);

            var li = LoadCurrentLocation(oi);

            var model = PreparePaymentInfoModel(customer, charge);
            return Json(GetJsonView(CommonValues.PaymentInfoViewName, model));
        }

        [HttpPost]
        [NopHttpsRequirement(SslRequirement.Yes)]
        public ActionResult PaymentInfo(PaymentInfoModel model, bool changeBillingAddress = false, bool checkNewCard = false)
        {
            var customer = _workContext.CurrentCustomer;
            if (model.CustomerId != customer.Id)
            {
                return AccessDeniedView();
            }
            if (model.StoreId < 1)
            {
                return AccessDeniedView();
            }
            if (!customer.IsRegistered())
            {
                return AccessDeniedView();
            }
            if (changeBillingAddress)
            {
                return PageRequest(CommonValues.BillingAddressViewName);
            }
            var oi = LoadOrderInformation(customer);
            var li = LoadCurrentLocation(oi);
            try
            {
                if (customer.BillingAddress == null)
                {
                    // Billing address missing
                    return RedirectToAction("BillingInfo");
                }
                if (oi == null || oi.OrderPaid)
                {
                    // Order status disallow payments
                    return RedirectToAction("Index");
                }

                var paymentExempt = _orderProcessingService.IsPaymentExempt(customer);
                var orderTotal = _orderProcessingService.CaculateTotal(oi, customer).Total;
                var debug = _logisticsInformationSettings.GpDebug;

                if (orderTotal <= 0 || paymentExempt || debug)
                {
                    _orderProcessingService.RemovePaymentInformation(customer);
                    oi.OrderCompleted = true;
                    _orderProcessingService.SaveOrderInformation(oi, customer);
                    return Json(GetJsonView(CommonValues.FinalizeViewName, null));
                }

                if (model.CreditCardId < 1 && !checkNewCard)
                    return AccessDeniedView();

                var paymentMethod = _paymentService.LoadPaymentMethodBySystemName("Payments.GlobalPayments");
                var creditCard = new PaymentCreditCard();
                if (checkNewCard)
                    creditCard = paymentMethod.GetCreditCard(0, model.StoreId, true);
                else
                    creditCard = paymentMethod.GetCreditCard(model.CreditCardId);

                if (creditCard == null)
                {
                    throw new Exception("no creditCard is found");
                }

                paymentMethod.SetDefaultCreditCard(creditCard.Id, customer.Id, model.StoreId);

                if (creditCard == null || creditCard.CustomerId != customer.Id)
                    return AccessDeniedView();

                var processPaymentRequest = new ProcessPaymentRequest()
                {
                    OrderTotal = orderTotal,
                    CustomerId = customer.Id,
                    StoreId = model.StoreId,
                    CreditCardId = creditCard.Id,
                    CreditCardType = creditCard.CreditCardType,
                    CreditCardNumber = creditCard.CreditCardNumber,
                    CreditCardName = creditCard.CreditCardName,
                    CreditCardExpireMonth = creditCard.CreditCardExpireMonth,
                    CreditCardExpireYear = creditCard.CreditCardExpireYear

                };

                oi.OrderCompleted = true;
                _orderProcessingService.SaveOrderInformation(oi, customer);
                _orderProcessingService.SavePaymentInformation(processPaymentRequest, customer);

                //return Json(GetJsonView(CommonValues.FinalizeViewName, null));
                return Json(GetJsonView(CommonValues.FinalizeViewName, null, null, null, -1, null));
            }
            catch (Exception e)
            {
                return FatalErrorView(e.Message, customer, e);
            }
        }

        [HttpPost]
        public ActionResult GetPaymentsCreditCard()
        {
            try
            {

                //var paymentMethodInst =
                //    _paymentService.LoadPaymentMethodBySystemName(
                //        _workContext.CurrentCustomer.SelectedPaymentMethodSystemName);
                //if (paymentMethodInst == null || !paymentMethodInst.IsPaymentMethodActive(_paymentSettings))
                //    throw new Exception("Selected payment method can't be parsed");
                // To do: get payment method based on order location
                var paymentMethod = _paymentService.LoadPaymentMethodBySystemName("Payments.GlobalPayments");
                if (paymentMethod == null)
                    throw new Exception("Payment method is not selected");
                var paymentsCreditCardModel = PaymentsCreditCardListModel(paymentMethod);

                return Json(GetJsonView(CommonValues.GetPaymentViewName, paymentsCreditCardModel));
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                return Json(new { error = 1, message = exc.Message });
            }
        }

        private ActionResult UpdateFinalizedOrder(Customer customer, OrderInformation oi)
        {
            if (customer == null)
                customer = _workContext.CurrentCustomer;
            if (oi == null)
                oi = LoadOrderInformation(customer);
            var pi = LoadPaymentInformation(customer);
            var paymentExempt = _orderProcessingService.IsPaymentExempt(customer);
            var charge = _orderProcessingService.CaculateTotal(oi, customer);
            var orderTotal = charge.Total;
            var debug = _logisticsInformationSettings.GpDebug;

            if (orderTotal <= 0 || paymentExempt || debug)
            {
                _orderProcessingService.RemovePaymentInformation(customer);
                oi.OrderCompleted = true;
                _orderProcessingService.SaveOrderInformation(oi, customer);
                return Json(GetJsonView(CommonValues.FinalizeViewName, null));
            }

            if (pi == null || !(pi.CreditCardId > 0) || pi.CustomerId != customer.Id)
            {
                // Requires new payment info
                _orderProcessingService.RemovePaymentInformation(customer);
                oi.OrderCompleted = false;
                _orderProcessingService.SaveOrderInformation(oi, customer);
                return CheckPayment(customer, oi, charge);
            }

            // Otherwise only update order total
            pi.OrderTotal = orderTotal;
            _orderProcessingService.SavePaymentInformation(pi, customer);

            oi.OrderCompleted = true;
            _orderProcessingService.SaveOrderInformation(oi, customer);
            return Json(GetJsonView(CommonValues.FinalizeViewName, null, null, null, -1, null));
        }

        [HttpPost]
        [NopHttpsRequirement(SslRequirement.Yes)]
        public ActionResult Finalize(int UserId = 0, int CreditCardId = 0, int packageCount = 0, decimal total = 0, int pickupAddressId = 0, int deliveryAddressId = 0)
        {

            ModelState.Clear();
            var customer = _workContext.CurrentCustomer;

            if (customer == null || !customer.IsRegistered())
            {
                return Json(GetJsonView(CommonValues.AccessDeniedViewName, null));
            }
            if (UserId != customer.Id)
            {
                // Customer mismatch
                ModelState.AddModelError("", "You are not authorized to process this order.");
                return Json(GetJsonView(CommonValues.FinalizeViewName, null));
            }

            var oi = LoadOrderInformation(customer);
            var li = LoadCurrentLocation(oi);
            var orderDetailModel = PrepareDetailViewModel(customer, li, oi.UserAddressId);
            if (orderDetailModel == null)
            {
                ModelState.AddModelError("", "Order information is invalid. Please check that all sections of your order request are correct.");
                return Json(GetJsonView(CommonValues.FinalizeViewName, null));
            }
            if (orderDetailModel.UserAddress == null || orderDetailModel.UserAddress.Id != pickupAddressId)
            {
                ModelState.AddModelError("", "The pickup address of this order is invalid. Please check that your address is correct and try again.");
                return Json(GetJsonView(CommonValues.FinalizeViewName, null));
            }
            if (orderDetailModel.UserAddress.Type == null || orderDetailModel.UserAddress.Type == AddressType.Unknown)
            {
                ModelState.AddModelError("", "The pickup address type is not set.");
                return Json(GetJsonView(CommonValues.FinalizeViewName, null));
            }

            if (orderDetailModel.Address == null || orderDetailModel.Address.Id != deliveryAddressId)
            {
                ModelState.AddModelError("", "The delivery address of this order is invalid. Please check that your address is correct and try again.");
                return Json(GetJsonView(CommonValues.FinalizeViewName, null));
            }
            if (orderDetailModel.Address.Type == null || orderDetailModel.Address.Type == AddressType.Unknown)
            {
                ModelState.AddModelError("", "The delivery address type is not set.");
                return Json(GetJsonView(CommonValues.FinalizeViewName, null));
            }
            if (pickupAddressId == deliveryAddressId)
            {
                ModelState.AddModelError("", "Pickup and delivery addresses cannot be the same.");
                return Json(GetJsonView(CommonValues.FinalizeViewName, null));
            }
            if (orderDetailModel.Packages == null || orderDetailModel.Packages.Count < 1 || orderDetailModel.Packages.Count != packageCount)
            {
                ModelState.AddModelError("", "Package information is invalid. Please go through the packages and try again.");
                return Json(GetJsonView(CommonValues.FinalizeViewName, null));
            }
            var paymentExempt = _orderProcessingService.IsPaymentExempt(customer);
            var orderTotal = _orderProcessingService.CaculateTotal(oi, customer).Total;
            var debug = _logisticsInformationSettings.GpDebug;
            var paymentRequired = !debug && !paymentExempt && orderTotal > 0;
            if (paymentRequired)
            {
                if (CreditCardId == 0)
                {
                    ModelState.AddModelError("", "You are not authorized to process this order.");
                    return Json(GetJsonView(CommonValues.FinalizeViewName, null));
                }
                var pi = LoadPaymentInformation(customer);


                //if (pi == null || pi.OrderTotal != total || pi.CreditCardId != CreditCardId || pi.CustomerId != UserId)

                var customerId = _workContext.CurrentCustomer.Id;

                _logger.Debug(string.Format("compare order creditCard Id,OrderTotal-pi.OrderTotal: {0}-{1},CreditCardId-pi.CreditCardId:{2}-{3},CustomerId-pi.CustomerId:{4}-{5},uid:{6}", total, pi.OrderTotal, CreditCardId, pi.CreditCardId, customerId, pi.CustomerId, UserId));

                if (pi == null || pi.OrderTotal != total || pi.CustomerId != customerId)
                {
                    ModelState.AddModelError("", "Your payment information does not match this order.");
                    return Json(GetJsonView(CommonValues.FinalizeViewName, null));
                }
            }

            int orderId = 0;

            

            try
            {
                var paymentMethod = _paymentService.LoadPaymentMethodBySystemName("Payments.GlobalPayments");
                var creditCard = new PaymentCreditCard();
                var invoice = new Invoice();

                // Place order; if need to validate payment, do not create shipments yet
                orderId = _orderProcessingService.PlaceOrder(oi, customer, null, !paymentRequired, paymentRequired);
                if (orderId <= 0)
                {
                    // Unlikely case where order placement failed
                    ModelState.AddModelError("", "Failed to create order.");
                    return Json(GetJsonView(CommonValues.FinalizeViewName, null));
                }
                else
                    LogCustomerActivity("Order " + orderId + " placed successfully.", customer, _customerActivityService);

                if (paymentRequired)
                {
                    var order = _orderService.GetOrderById(orderId);
                    if (order.PaymentStatus != PaymentStatus.Paid)
                    {
                        creditCard = paymentMethod.GetCreditCard(CreditCardId);
                        if (creditCard == null || creditCard.CustomerId != UserId || String.IsNullOrEmpty(creditCard.Token))
                        {
                            ModelState.AddModelError("", "Credit card is invalid.");
                            return Json(GetJsonView(CommonValues.FinalizeViewName, null));
                        }
                        var message = "";
                        if (!PayOrder(_orderService.GetOrderById(orderId), creditCard.Id, creditCard.Token, out message))
                        {
                            ModelState.AddModelError("", "Order created, but payment process is unsuccessful.");
                            ModelState.AddModelError("", message);
                            return Json(GetJsonView(CommonValues.FinalizeViewName, null));
                        }
                    }
                    // Successfully paid, create shipments
                    orderId = _orderProcessingService.PlaceOrder(oi, customer, null, true);

                    LogCustomerActivity("Payment processed successfully.", customer, _customerActivityService);

                    //create invoice
                    _invoiceService.CreateInvoice(orderId, customer, InvoiceStatus.PaidByCreditCard);
                }

                //send email
                _orderProcessingService.SendLogisticPlacedNotification(orderId);

                if (oi.Locations.FirstOrDefault().Packages.Count == 1 && !paymentExempt && orderTotal == 0)
                {
                    //create invoice
                    _invoiceService.CreateInvoice(orderId, customer, InvoiceStatus.Paid);
                }
            }
            catch (Exception e)
            {
                ModelState.AddModelError("", e.Message);
                return Json(GetJsonView(CommonValues.FinalizeViewName, null));
            }

            // Order complete, go to complete page
            var completeModel = new OrderCompleteModel()
            {
                OrderId = orderId
            };
            if (li.PickupDate.HasValue)
                completeModel.PickupDate = li.PickupDate.Value;

            var pkgs = new List<LogisticsPackage>();
            oi.Locations.ForEach(l => pkgs.AddRange(
            l.Packages.Where(p => p.PackageId.HasValue)
                           .Select(p => _logisticsService.GetPackageById(p.PackageId.Value))
            ));
            var packages = pkgs.Select(PreparePackageModelForList).ToList();

            foreach (var package in packages)
            {
                completeModel.Packages.Add(new PackageInfo()
                {
                    Id = package.Id,
                    Status = package.Status,
                    TrackingNumber = package.TrackingNumber,
                    Measurement = package.PackageMeasurement
                });
            }
            _orderProcessingService.RemoveCurrentOrder(customer);
            _orderProcessingService.RemovePaymentInformation(customer);
            return Json(GetJsonView(CommonValues.CompleteViewName, completeModel, null, null, -1, null, true));
        }

        [HttpPost]
        public ActionResult AjaxCostCalculate(OrderPackageModel model)
        {
            var customer = _workContext.CurrentCustomer;
            var oi = LoadOrderInformation(customer);
            var li = oi.Locations
                .Single(l => l.Id == oi.CurrentLocationId);
            decimal cost = 0;
            var summodel = new SummaryModel();
            // TODO: remove duplicate code within Package
            try
            {
                if (ModelState.IsValid)
                {
                    if (model.PackagePreset != null) //(model.IsLettersize)
                    {
                        model.Length = null;
                        model.Width = null;
                        model.Height = null;
                        model.Width = null;
                        model.DimensionMeasureId = _measureSettings.BaseDimensionId;
                        model.WeightMeasureId = _measureSettings.BaseWeightId;
                    }
                    else if (model.Length.HasValue && model.Width.HasValue && model.Height.HasValue)
                    {
                        var dim = _measureService.GetMeasureDimensionById(model.DimensionMeasureId);
                        if (dim != null)
                        {
                            var max = MathEx.Round(_measureService.ConvertFromPrimaryMeasureDimension(
                                _logisticsInformationSettings.MaxDimensionSize, dim), 0);
                            decimal total = 0;
                            total += model.Length.Value + model.Width.Value + model.Height.Value;
                            if (total > max)
                            {
                                ModelState.AddModelError("", String.Format(_localizationService.GetResource("Logistics.Order.Package.Maximumsize"), (int)max, dim.Name));
                            }
                        }
                    }
                }

                if (ModelState.IsValid)
                {
                    if (li.Packages != null && li.Packages.Exists(p => p.Id == model.PackageId))
                    {
                        var package = li.Packages.Single(p => p.Id == model.PackageId);
                        var prevmodel = PreparePackageModel(package, li.Packages.Where(p => p.Id != model.PackageId));
                        li = UpdatePackage(model, customer);
                        cost = CalculatePackageCost(li, customer, model.PackageId);
                        summodel = PrepareSummaryModel(customer);
                        li = UpdatePackage(prevmodel, customer);
                    }
                    else
                    {
                        li = AddPackage(model, customer);
                        cost = CalculatePackageCost(li, customer, model.PackageId);
                        summodel = PrepareSummaryModel(customer);
                        RemovePackage(model.PackageId);
                    }
                }
            }
            catch
            {
            }
            summodel.CurrentPackageTotal = cost;
            summodel.SubTotalExcCurrentPackage = summodel.SubTotalExcSurchargeTax - cost;
            return Json(new
            {
                sumhtml = this.RenderPartialViewToString(CommonValues.SummaryViewName, summodel),
                Cost = cost
            });
        }

        [HttpPost]
        [ValidateInput(false)]
        public ActionResult Detail(FormCollection form)
        {
            var customer = _workContext.CurrentCustomer;
            var oi = LoadOrderInformation(customer);
            bool? isGiftCardValid = null;

            var li = oi.Locations
                .Single(l => l.Id == oi.CurrentLocationId);

            if (!String.IsNullOrEmpty(form[CommonValues.AddMoreLocationFormKey]))
            {
                var model = PrepareLocationModel(customer, null, oi.UserAddressId);
                //model.LocationEdit = true;
                return Json(GetJsonView(CommonValues.LocationInfoViewName, model));
            }
            else if (!String.IsNullOrEmpty(form[CommonValues.EditAddressFormKey]))
            {
                var model = PrepareLocationModel(customer, li, oi.UserAddressId, true);
                _orderProcessingService.SaveOrderInformation(oi, customer);
                return Json(GetJsonView(CommonValues.LocationInfoViewName, model));
            }
            else if (!String.IsNullOrEmpty(form[CommonValues.EditPackageFormKey]))
            {
                string pkgIdStr = form[CommonValues.EditPackageFormKey];
                int pkgId;
                if (int.TryParse(pkgIdStr, out pkgId))
                {
                    var pi = li.Packages.Single(p => p.Id == pkgId);
                    OrderPackageModel pkgModel = PreparePackageModel(pi, li.Packages.Where(p => p.Id != pi.Id));
                    pkgModel.InsuranceEnabled = InsuranceEnabled(li);
                    pkgModel.ShowNextPackage = false;
                    pkgModel.PackageCount = li.PackageNumber;
                    pkgModel.Cost = CalculatePackageCost(li, customer, pi.Id);
                    return Json(GetJsonView(CommonValues.PackageInfoViewName, pkgModel, null, null, pkgModel.PackageId));
                }
            }
            else if (!String.IsNullOrEmpty(form[CommonValues.RemovePackageFormKey]))
            {
                string pkgIdStr = form[CommonValues.RemovePackageFormKey];
                int pkgId;
                if (int.TryParse(pkgIdStr, out pkgId))
                {
                    return RemovePackage(pkgId);
                }
            }
            else if (!String.IsNullOrEmpty(form[CommonValues.PrintLabelFormKey]))
            {
                string prepareLabelStr = form[CommonValues.PrintLabelFormKey];
                if (prepareLabelStr.IndexOf("true", StringComparison.InvariantCultureIgnoreCase) >= 0)
                {
                    li.Packages.ForEach(p => p.AddtionalAttributes &= ~AddtionalAttributes.NoLabel);
                }
                else
                {
                    li.Packages.ForEach(p => p.AddtionalAttributes |= AddtionalAttributes.NoLabel);
                }
                _orderProcessingService.SaveOrderInformation(oi, customer);
            }
            else if (!String.IsNullOrEmpty(form[CommonValues.CheckoutFormKey]))
            {
                return Json(new { name = CommonValues.CheckoutName });
            }
            else if (!String.IsNullOrEmpty(form[CommonValues.EditOrderByInfo]))
            {
                var model = new SummaryModel.LocationModel();
                TryUpdateModel(model);
                if (!String.IsNullOrEmpty(form[CommonValues.EditOrderByInfo]))
                {
                    // TODO: Remove the hard code and add order by info validation.
                    if (!String.IsNullOrEmpty(model.OrderByInfo) && model.OrderByInfo.Length < 256)
                    {
                        li.OrderInfo = model.OrderByInfo;
                        _orderProcessingService.SaveOrderInformation(oi, customer);
                    }
                }
            }
            else if (!String.IsNullOrEmpty(form[CommonValues.ApplyGiftCardCouponCodeKey]))
            {
                var giftcardcouponcode = form[CommonValues.GiftCardCouponCodeKey];


                if (!String.IsNullOrEmpty(giftcardcouponcode))
                {
                    var appliedCard = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer)
                        .FirstOrDefault();
                    if (appliedCard != null && appliedCard.GiftCardCouponCode != giftcardcouponcode)
                    {
                        isGiftCardValid = false;
                    }
                    else
                    {
                        var giftCard = _giftCardService.GetAllGiftCards(null, null,
                            null, null, giftcardcouponcode, 0, int.MaxValue).FirstOrDefault();
                        isGiftCardValid = giftCard != null
                            && giftCard.GiftCardType == GiftCardType.ExpressPackage
                            && giftCard.IsGiftCardValid();
                        if (isGiftCardValid.Value)
                        {
                            customer.ApplyGiftCardCouponCode(giftcardcouponcode);
                            _customerService.UpdateCustomer(customer);
                            // Remove the insurance value
                            var pkg = li.Packages.FirstOrDefault();
                            if (pkg != null)
                            {
                                pkg.InsuranceValue = null;
                                pkg.AddtionalAttributes &= ~AddtionalAttributes.Insurance;

                                _orderProcessingService.SaveOrderInformation(oi, customer);
                            }
                        }
                    }
                }
            }

            var detailModel = PrepareDetailViewModel(customer, li, oi.UserAddressId);
            return Json(GetJsonView(CommonValues.DetailViewName, detailModel, null, null, -1, isGiftCardValid));
        }

        [HttpPost]
        public ActionResult CancelCurrentLocation()
        {
            var customer = _workContext.CurrentCustomer;
            try
            {
                var oi = _orderProcessingService.LoadOrderInformation(customer);
                if (oi != null)
                {
                    if (oi.CurrentLocationId > 0)
                    {
                        if (oi.OrderPaid)
                        {
                            return FatalErrorJsonView("The order has been checked out.", customer);
                        }

                        oi.CurrentLocationId -= 1;
                        if (oi.CurrentLocationId > 0)
                        {
                            oi.OrderCompleted = true;
                            oi.Locations.RemoveAt(oi.Locations.Count - 1);
                            _orderProcessingService.SaveOrderInformation(oi, customer);
                            if (oi.CurrentLocationId > 0)
                            {
                                var detailModel = PrepareDetailViewModel(customer, oi.Locations.Single(l => l.Id == oi.CurrentLocationId), oi.UserAddressId);
                                return Json(GetJsonView(CommonValues.DetailViewName, detailModel));
                            }
                        }
                        _orderProcessingService.RemoveCurrentOrder(customer);
                    }
                }

                InitOrder(customer);
                var model = PrepareLocationModel(customer, null, oi.UserAddressId);
                return Json(GetJsonView(CommonValues.LocationInfoViewName, model));
                //return RedirectToAction("OrderInfo");
            }
            catch (Exception e)
            {
                return FatalErrorJsonView("The current location cannot be deleted.", customer, e);
            }
        }

        [NonAction]
        private ActionResult RemovePackage(int packageId)
        {
            var customer = _workContext.CurrentCustomer;
            try
            {
                var oi = LoadOrderInformation(customer);
                var li = oi.Locations.SingleOrDefault(l => l.Id == oi.CurrentLocationId);

                if (li == null || li.Packages == null)
                {
                    return FatalErrorJsonView(
                        String.Format("The package (ID:{0}) cannot be removed. Location (ID:{1}) cannot be found.", packageId, oi.CurrentLocationId),
                        customer);
                }

                var packageCount = li.Packages.Count;

                if (packageCount < 1)
                {
                    return FatalErrorJsonView(
                        String.Format("The package (ID:{0}) cannot be removed. Package total number is {1}", packageId, packageCount),
                        customer);
                }

                if (!li.Packages.Any(p => p.Id == packageId))
                {
                    return Json(FatalErrorJsonView(
                        String.Format("The package (ID:{0}) cannot be found. Package total number is {1}", packageId, packageCount),
                        customer));
                }

                li.Packages.RemoveAt(packageId - 1);

                if (li.Packages.Count > 0)
                {
                    foreach (var pkg in li.Packages)
                    {
                        if (pkg.Id > packageId)
                        {
                            pkg.Id -= 1;
                        }
                    }
                    _orderProcessingService.SaveOrderInformation(oi, customer);
                    var detailModel = PrepareDetailViewModel(customer, li, oi.UserAddressId);
                    //return RedirectToAction("OrderInfo");
                    return Json(GetJsonView(CommonValues.DetailViewName, detailModel));
                }

                if (li.Packages.Count != 0)
                {
                    return FatalErrorJsonView(String.Format("The package (ID:{0}) cannot be removed.", packageId));
                }

                oi.OrderCompleted = false;

                var model = PrepareLocationModel(customer, li, oi.UserAddressId, true);

                _orderProcessingService.SaveOrderInformation(oi, customer);
                return Json(GetJsonView(CommonValues.LocationInfoViewName, model));
            }
            catch (Exception e)
            {
                return FatalErrorJsonView(String.Format("The package (ID:{0}) cannot be removed.", packageId), customer, e);
            }
        }

        #endregion

        protected int GetStoreId()
        {
            if (_logisticsInformationSettings.LogisticsServiceStoreId.HasValue)
                return _logisticsInformationSettings.LogisticsServiceStoreId.Value;
            return Nop.Core.Domain.Stores.Store.AllStoreReservedId;
        }
        [NonAction]
        protected OrderPaymentinfoModel PaymentsCreditCardListModel(IPaymentMethod paymentMethod, int? storeId = null)
        {
            var model = new OrderPaymentinfoModel();
            string actionName;
            string controllerName;
            RouteValueDictionary routeValues;
            paymentMethod.GetPaymentInfoRoute(out actionName, out controllerName, out routeValues);
            model.PaymentInfoActionName = actionName;
            model.PaymentInfoControllerName = controllerName;
            model.PaymentInfoRouteValues = routeValues;
            model.PaymentInfoRouteValues.Add("type", "logistics");
            model.ShowSubmitButton = true;
            model.PaymentInfoRouteValues.Add("customerId", _workContext.CurrentCustomer.Id);
            model.PaymentInfoRouteValues.Add("storeId", GetStoreId());
            //model.DisplayOrderTotals = _orderSettings.OnePageCheckoutDisplayOrderTotalsOnPaymentInfoTab;
            return model;
        }
        #region Payment

        [NopHttpsRequirement(SslRequirement.Yes)]
        public ActionResult BillingInfo()
        {
            var customer = _workContext.CurrentCustomer;
            var model = new BillingInfoModel();
            var ba = customer.BillingAddress;

            if (ba == null)
            {
                model.ShowSameAsShipping = true;
                model.BillingAddress = new AddressModel();
            }
            else
            {
                model.ShowSameAsShipping = false;
                model.BillingAddress = PrepareAddressModel(ba);
            }

            return View(model);
        }

        [HttpPost]
        public ActionResult BillingInfo(BillingInfoModel model)
        {
            var customer = _workContext.CurrentCustomer;
            if (ModelState.IsValid)
            {
                if (model.IsSameAsShipping && customer.BillingAddress == null)
                {
                    customer.BillingAddress = customer.ShippingAddress;
                }
                else
                {
                    AddressModelToAddress(model.BillingAddress, customer.BillingAddress);
                }
                _customerService.UpdateCustomer(customer);
                return RedirectToAction("PaymentInfo");
            }

            model.ShowSameAsShipping = customer.BillingAddress == null;
            return View(model);
        }
        /*
        [NopHttpsRequirement(SslRequirement.Yes)]
        public ActionResult PaymentInfo(bool? isGiftCardValid = null, bool? checkPaymentExempt = null)
        {
            var customer = _workContext.CurrentCustomer;
            if (!customer.IsRegistered())
            {
                return AccessDeniedView();
            }


            if (checkPaymentExempt != false && _orderProcessingService.IsPaymentExempt(customer))
            {
                return RedirectToAction("Index");
            }

            if (customer.BillingAddress == null)
            {
                return RedirectToAction("BillingInfo");
            }

            try
            {
                var oi = LoadOrderInformation(customer);

                if (oi == null || !oi.OrderCompleted || oi.OrderPaid)
                {
                    return RedirectToAction("Index");
                }

                var total = _orderProcessingService.CaculateTotal(oi, customer).Total;
                if (total <= 0)
                {
                    return RedirectToAction("Confirm");
                }

                var processPaymentRequest = new ProcessPaymentRequest()
                {
                    OrderTotal = total
                };

                _orderProcessingService.SavePaymentInformation(processPaymentRequest, customer);
                return View("PaymentInfo", PrepareSummaryModel(customer, _logisticsInformationSettings.EnableGiftCard, isGiftCardValid));
            }
            catch (Exception e)
            {
                return FatalErrorView(e.Message, customer, e);
            }
        }
        *
        [HttpPost]
        [ValidateInput(false)]
        public ActionResult ApplyGiftCard(string giftCardCouponCode)
        {
            var customer = _workContext.CurrentCustomer;
            bool isGiftCardValid = false;

            var appliedCard = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer)
                .FirstOrDefault();
            if (appliedCard != null && appliedCard.GiftCardCouponCode != giftCardCouponCode)
            {
                isGiftCardValid = false;
            }
            else if (!String.IsNullOrEmpty(giftCardCouponCode))
            {
                var giftCard = _giftCardService.GetAllGiftCards(null, null,
                    null, null, giftCardCouponCode, 0, int.MaxValue).FirstOrDefault();
                isGiftCardValid = giftCard != null
                    && giftCard.GiftCardType == GiftCardType.ExpressPackage
                    && giftCard.IsGiftCardValid();
            }

            if (isGiftCardValid)
            {
                customer.ApplyGiftCardCouponCode(giftCardCouponCode);
                _customerService.UpdateCustomer(customer);

                var oi = _orderProcessingService.LoadOrderInformation(customer);
                if (oi != null)
                {
                    var pkg = oi.Locations.First().Packages.First();
                    pkg.InsuranceValue = null;
                    pkg.AddtionalAttributes &= ~AddtionalAttributes.Insurance;

                    _orderProcessingService.SaveOrderInformation(oi, customer);
                }
            }

            return PaymentInfo(isGiftCardValid);
        }

        [HttpPost]
        public ActionResult PaymentInfo(FormCollection form)
        {
            var customer = _workContext.CurrentCustomer;
            try
            {
                if (ModelState.IsValid)
                {
                    if (form["succeeded"] == "succeeded")
                    {
                        LogCustomerActivity("Payment processed successfully.", customer, _customerActivityService);
                        _orderProcessingService.RemovePaymentInformation(customer);

                        try
                        {
                            _httpContext.Session[PaymentContants.PaymentInfoSessionKey] = new ProcessPaymentRequest()
                            {
                                CreditCardNumber = form["ccNumber"],
                                CreditCardType = form["ccType"],
                                CreditCardExpireMonth = int.Parse(form["expMonth"]),
                                CreditCardExpireYear = int.Parse(form["expYear"]),
                                CreditCardName = form["name"]
                            };

                        }
                        catch (Exception)
                        {
                            _httpContext.Session[PaymentContants.PaymentInfoSessionKey] = null;
                        }

                        var oi = LoadOrderInformation(customer);
                        oi.OrderPaid = true;
                        _orderProcessingService.SaveOrderInformation(oi, customer);

                        _httpContext.Session[PaymentContants.SummarySessionKey] = PrepareSummaryModel(customer);

                        var orderId = _orderProcessingService.PlaceOrder(oi, customer);
                        if (orderId <= 0)
                        {
                            LogError(String.Format("Invalid Order ID: {0}.", orderId));
                        }

                        var invoice = _invoiceService.CreateInvoice(orderId, customer, InvoiceStatus.PaidByCreditCard);

                        // handle return request scenario
                        try
                        {
                            var pendingReturnRequests = customer.GetAttribute<List<ReturnRequest>>(SystemCustomerAttributeNames.PendingReturnRequests);
                            if (pendingReturnRequests != null && pendingReturnRequests.Count > 0)
                            {
                                pendingReturnRequests.ForEach(x => _workContext.CurrentCustomer.ReturnRequests.Add(x));
                                _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                                pendingReturnRequests.ForEach(x => _workflowMessageService.SendNewReturnRequestStoreOwnerNotification(
                                    x, _orderService.GetOrderProductVariantById(x.OrderProductVariantId), _localizationSettings.DefaultAdminLanguageId));
                            }
                        }
                        catch (Exception exc)
                        {
                            return FatalErrorView("Customer paid for return shipment, but the request requests haven't been created.", customer, exc);
                        }

                        return RedirectToAction("OrderConfirm");
                    }
                }

                //If we got this far, something failed, redisplay form
                return View(PrepareSummaryModel(customer));
            }
            catch (Exception e)
            {
                return FatalErrorView(e.Message, customer, e);
            }
        }
        */
        #endregion

        #region Confirmation

        public ActionResult OrderConfirm()
        {
            var customer = _workContext.CurrentCustomer;
            try
            {
                if (!customer.IsRegistered())
                {
                    return AccessDeniedView();
                }

                var processPaymentRequest = _httpContext.Session[PaymentContants.PaymentInfoSessionKey] as ProcessPaymentRequest;
                var model = _httpContext.Session[PaymentContants.SummarySessionKey] as SummaryModel;

                if (processPaymentRequest != null && processPaymentRequest.CreditCardNumber != null
                    && model != null)
                {
                    model.PaymentInfo = new SummaryModel.PaymentInfoType()
                    {
                        CardNumber = processPaymentRequest.CreditCardNumber,
                        CardType = processPaymentRequest.CreditCardType,
                        ExpMonth = processPaymentRequest.CreditCardExpireMonth,
                        ExpYear = processPaymentRequest.CreditCardExpireYear,
                        CardHolder = processPaymentRequest.CreditCardName
                    };
                    _httpContext.Session[PaymentContants.PaymentInfoSessionKey] = null;

                    // clear the return request flag

                    var pendingReturnRequests = customer.GetAttribute<List<ReturnRequest>>(SystemCustomerAttributeNames.PendingReturnRequests);
                    if (pendingReturnRequests != null && pendingReturnRequests.Count > 0)
                    {
                        _genericAttributeService.SaveAttribute<List<ReturnRequest>>(customer, SystemCustomerAttributeNames.PendingReturnRequests, null);
                        ViewBag.IsReturnRequest = true;
                    }

                    return View(model);
                }
                return RedirectToAction("PaymentInfo");
            }
            catch (Exception e)
            {
                return FatalErrorView(e.Message, customer, e);
            }
        }

        public ActionResult Confirm(int creditCardId)
        {
            var customer = _workContext.CurrentCustomer;
            if (!customer.IsRegistered())
            {
                return AccessDeniedView();
            }

            var profiler = MiniProfiler.Current;

            try
            {
                var oi = LoadOrderInformation(customer);

                if (oi == null || !oi.OrderCompleted)
                {
                    return RedirectToAction("Index");
                }

                LogCustomerActivity("Order confirmed by customer", customer, _customerActivityService);

                // Process order
                if (!oi.OrderPaid)
                {
                    if (_orderProcessingService.IsPaymentExempt(customer)
                        || _orderProcessingService.CaculateTotal(oi, customer).Total <= 0)
                    {
                        //skip payment
                        oi.OrderPaid = true;
                        var model = PrepareSummaryModel(customer, creditCardId);
                        using (profiler.Step("Confirm SaveOrderInformation"))
                        {
                            _orderProcessingService.SaveOrderInformation(oi, customer);
                        }

                        using (profiler.Step("Confirm PlaceOrder"))
                        {
                            _orderProcessingService.PlaceOrder(oi, customer);
                        }

                        return View(model);
                    }
                    else
                    {
                        return RedirectToAction("PaymentInfo");
                    }
                }

                return RedirectToAction("Index");
            }
            catch (Exception e)
            {
                return FatalErrorView(e.Message, customer, e);
            }
        }


        #endregion

        #region private
        /// <summary>
        /// payorder
        /// </summary>
        /// <param name="order">order</param>
        /// <returns></returns>
        private bool PayOrder(Order order, int creditCardId, string tokenId, out string message)
        {
            //var url = "https://direct.gtpaysecure.net/direct/services/authorize/?CRESecureID={0}&CRESecureAPIToken={1}&total_amt={2}&CRE_Card_Token= {3}&customer_id={4}&orderId={5}&pass_thru=true";
            //var CRESecureID = "gt694878820888SB";
            //var CRESecureAPIToken = "c341fa747b8a79d49f6ec94fb25bd15f";
            //var totalAmt = "1.00";
            //var creCardToken = "f0f3469de56dffc394e62d6435663066232a6d22";
            //var customerId = _workContext.CurrentCustomer.Id;
            ////var orderId=orderId
            //url=string.Format(url, CRESecureID, CRESecureAPIToken, totalAmt, creCardToken, customerId, orderId);
            var paymentMethod = _paymentService.LoadPaymentMethodBySystemName("Payments.GlobalPayments");
            message = "";
            return paymentMethod.AddPaymentsTransaction(order.Id, order.OrderTotal, creditCardId, tokenId, out message);

            //var rp = new RefundPaymentRequest
            //{
            //    Order = order,
            //    AmountToRefund = order.OrderTotal,
            //    IsPartialRefund = true
            //};
            //var pay = paymentMethod.Refund(rp);
            //if (pay.Success)
            //{
            //    return true;
            //}
            //return false;
            //var aa = paymentMethod.SendRequest();
        }
        #endregion

        #region Download

        public ActionResult DownloadWaybill(int? id, int? shipmentId, bool? isReturnRequest)
        {
            if (!_workContext.CurrentCustomer.IsRegistered())
            {
                return AccessDeniedView();
            }

            try
            {
                var pkg = id.HasValue ? _logisticsService.GetPackageById(id.Value) : shipmentId.HasValue ? _logisticsService.GetPackageByShipmentId(shipmentId.Value) : null;
                if (pkg == null)
                {
                    if (isReturnRequest.HasValue && isReturnRequest.Value)
                    {
                        string returnUrl = _workContext.CurrentCustomer.GetAttribute<string>(SystemCustomerAttributeNames.LastContinueShoppingPage);
                        string continueShoppingUrl;
                        if (!String.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
                        {
                            continueShoppingUrl = returnUrl;
                        }
                        else
                        {
                            continueShoppingUrl = Url.RouteUrl("HomePage");
                        }
                        ViewBag.ContinueShoppingUrl = continueShoppingUrl;
                    }
                    // List all packages
                    return View(PrepareDownloadModel());
                }
                else
                {
                    //Download
                    var trackingUrl = Url.RouteUrl("LogisticsTrack", new { pid = pkg.Id }, Request.Url.Scheme);

                    bool isReturnRequestLogisticOrder = false;
                    if (!shipmentId.HasValue)
                    {
                        isReturnRequestLogisticOrder = IsReturnRequestLogisticOrder(pkg);
                    }

                    byte[] bytes = null;
                    using (var stream = new System.IO.MemoryStream())
                    {
                        _pdfService.PrintLogisticsWayBillToPdfWithReturn(stream, pkg, trackingUrl, _workContext.WorkingLanguage, isReturnRequestLogisticOrder);
                        bytes = stream.ToArray();
                    }
                    var filename = WaybillFilename.GenerateWaybillFilename(pkg.Shipment.TrackingNumber);
                    Response.AppendHeader("Content-Disposition", "inline; filename=" + filename);

                    return File(bytes, "application/pdf");
                }
            }
            catch (Exception e)
            {
                return FatalErrorView(e.Message, null, e);
            }
        }

        private bool IsReturnRequestLogisticOrder(LogisticsPackage pkg)
        {
            try
            {
                if (pkg == null) return false;

                var shipment = _shipmentService.GetShipmentById(pkg.ShipmentId);

                if (shipment == null || shipment.OrderId <= 0) return false;

                var order = _orderService.GetOrderById(shipment.OrderId);

                var genergies = _genericAttributeService.GetAttributesForEntity(_workContext.CurrentCustomer.Id, ReturnRequestGeneryKeyGroup);

                return genergies.Any(item => item.Value.Equals(order.Id.ToString()));
            }
            catch (Exception exception)
            {
                _logger.Error("IsReturnRequestLogisticOrder,error: " + exception.ToString());
                return false;
            }
        }

        #endregion

        #endregion
    }
}
